Skip to content

Commit

Permalink
Merge pull request vmware-tanzu#791 from akutz/feature/sysprep-api-fi…
Browse files Browse the repository at this point in the history
…elds

🐛 Update VM API spec.bootstrap.sysprep fields
  • Loading branch information
akutz authored Nov 4, 2024
2 parents ebb27ab + 3eab5a0 commit d107a82
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 42 deletions.
40 changes: 39 additions & 1 deletion api/v1alpha1/virtualmachine_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ func TestVirtualMachineConversion(t *testing.T) {
g.Expect(apiequality.Semantic.DeepEqual(hubBefore, hubAfter)).To(BeTrue(), cmp.Diff(hubBefore, hubAfter))
}

hubSpokeHubEx := func(g *WithT, hub, hubAfter ctrlconversion.Hub, spoke ctrlconversion.Convertible) {
hubBefore := hub.DeepCopyObject().(ctrlconversion.Hub)

// First convert hub to spoke
dstCopy := spoke.DeepCopyObject().(ctrlconversion.Convertible)
g.Expect(dstCopy.ConvertFrom(hubBefore)).To(Succeed())

// Convert spoke back to hub and check if the resulting hub is equal to the hub before the round trip
g.Expect(dstCopy.ConvertTo(hubAfter)).To(Succeed())

g.Expect(apiequality.Semantic.DeepEqual(hubBefore, hubAfter)).To(BeTrue(), cmp.Diff(hubBefore, hubAfter))
}

spokeHubSpoke := func(g *WithT, spoke ctrlconversion.Convertible, hub ctrlconversion.Hub) {
spokeBefore := spoke.DeepCopyObject().(ctrlconversion.Convertible)

Expand Down Expand Up @@ -375,7 +388,7 @@ func TestVirtualMachineConversion(t *testing.T) {
Identification: &vmopv1sysprep.Identification{
DomainAdmin: "my-admin",
},
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
FullName: "vmware",
},
},
Expand All @@ -387,6 +400,31 @@ func TestVirtualMachineConversion(t *testing.T) {
hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{})
})

t.Run("VirtualMachine and partial inline sysprep", func(t *testing.T) {
t.Run("hub-spoke-hub", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{
Sysprep: &vmopv1.VirtualMachineBootstrapSysprepSpec{
Sysprep: &vmopv1sysprep.Sysprep{
UserData: vmopv1sysprep.UserData{
FullName: "test-user",
OrgName: "test-org",
ProductID: &vmopv1sysprep.ProductIDSecretKeySelector{
Key: "product_id",
Name: "vm-w9w4mn9xr2",
},
},
},
},
},
},
}
hubSpokeHubEx(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a1.VirtualMachine{})
})
})

t.Run("VirtualMachine hub-spoke-hub with Sysprep and vAppConfig", func(t *testing.T) {
g := NewWithT(t)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ func Convert_sysprep_Sysprep_To_sysprep_Sysprep(
}
out.GUIUnattended = (*vmopv1sysprep.GUIUnattended)(unsafe.Pointer(in.GUIUnattended))
out.LicenseFilePrintData = (*vmopv1sysprep.LicenseFilePrintData)(unsafe.Pointer(in.LicenseFilePrintData))
out.UserData = (*vmopv1sysprep.UserData)(unsafe.Pointer(in.UserData))
if in.UserData != nil {
out.UserData.FullName = in.UserData.FullName
out.UserData.OrgName = in.UserData.OrgName
out.UserData.ProductID = (*vmopv1sysprep.ProductIDSecretKeySelector)(unsafe.Pointer(in.UserData.ProductID))
}
if id := in.Identification; id != nil {
out.Identification = &vmopv1sysprep.Identification{
DomainAdmin: id.DomainAdmin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ func Convert_sysprep_Sysprep_To_sysprep_Sysprep(
}
out.GUIUnattended = (*vmopv1a2sysprep.GUIUnattended)(unsafe.Pointer(in.GUIUnattended))
out.LicenseFilePrintData = (*vmopv1a2sysprep.LicenseFilePrintData)(unsafe.Pointer(in.LicenseFilePrintData))
out.UserData = (*vmopv1a2sysprep.UserData)(unsafe.Pointer(in.UserData))
out.UserData = &vmopv1a2sysprep.UserData{
FullName: in.UserData.FullName,
OrgName: in.UserData.OrgName,
ProductID: (*vmopv1a2sysprep.ProductIDSecretKeySelector)(unsafe.Pointer(in.UserData.ProductID)),
}
if id := in.Identification; id != nil {
out.Identification = &vmopv1a2sysprep.Identification{
DomainAdmin: id.DomainAdmin,
Expand Down
4 changes: 2 additions & 2 deletions api/v1alpha2/virtualmachine_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestVirtualMachineConversion(t *testing.T) {
Identification: &vmopv1sysprep.Identification{
DomainAdmin: "my-admin",
},
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
FullName: "vmware",
},
},
Expand Down Expand Up @@ -591,7 +591,7 @@ func TestVirtualMachineConversion(t *testing.T) {
Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{
Sysprep: &vmopv1.VirtualMachineBootstrapSysprepSpec{
Sysprep: &vmopv1sysprep.Sysprep{
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
FullName: "test-user",
OrgName: "test-org",
ProductID: &vmopv1sysprep.ProductIDSecretKeySelector{
Expand Down
11 changes: 6 additions & 5 deletions api/v1alpha3/sysprep/sysprep.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ type Sysprep struct {
// Server 2003.
LicenseFilePrintData *LicenseFilePrintData `json:"licenseFilePrintData,omitempty"`

// +optional

// UserData is a representation of the Sysprep UserData key.
UserData *UserData `json:"userData,omitempty"`
UserData UserData `json:"userData"`
}

// GUIRunOnce maps to the GuiRunOnce key in the sysprep.xml answer file.
Expand Down Expand Up @@ -98,13 +96,16 @@ type GUIUnattended struct {
// `password`.
Password *PasswordSecretKeySelector `json:"password,omitempty"`

// +optional
// +kubebuilder:default=85
// +kubebuilder:validation:Minimum=0

// TimeZone is the time zone index for the virtual machine.
//
// Please note that numbers correspond to time zones listed at
// https://bit.ly/3Rzv8oL.
TimeZone int32 `json:"timeZone,omitempty"`
//
// Defaults to UTC.
TimeZone int32 `json:"timeZone"`
}

// PasswordSecretKeySelector references the password value from a Secret resource
Expand Down
6 changes: 1 addition & 5 deletions api/v1alpha3/sysprep/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -717,13 +717,19 @@ spec:
- name
type: object
timeZone:
default: 85
description: |-
TimeZone is the time zone index for the virtual machine.
Please note that numbers correspond to time zones listed at
https://bit.ly/3Rzv8oL.
Defaults to UTC.
format: int32
minimum: 0
type: integer
required:
- timeZone
type: object
identification:
description: Identification is a representation
Expand Down Expand Up @@ -827,6 +833,8 @@ spec:
- fullName
- orgName
type: object
required:
- userData
type: object
type: object
vAppConfig:
Expand Down
8 changes: 8 additions & 0 deletions config/crd/bases/vmoperator.vmware.com_virtualmachines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3504,13 +3504,19 @@ spec:
- name
type: object
timeZone:
default: 85
description: |-
TimeZone is the time zone index for the virtual machine.
Please note that numbers correspond to time zones listed at
https://bit.ly/3Rzv8oL.
Defaults to UTC.
format: int32
minimum: 0
type: integer
required:
- timeZone
type: object
identification:
description: Identification is a representation of the
Expand Down Expand Up @@ -3613,6 +3619,8 @@ spec:
- fullName
- orgName
type: object
required:
- userData
type: object
type: object
vAppConfig:
Expand Down
18 changes: 12 additions & 6 deletions pkg/providers/vsphere/sysprep/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@ func GetSysprepSecretData(
productID, password, domainPwd string
)

if userData := in.UserData; userData != nil && userData.ProductID != nil {
if in.UserData.ProductID != nil {
// this is an optional secret key selector even when FullName or OrgName are set.
err := util.GetSecretData(ctx, k8sClient, secretNamespace, userData.ProductID.Name, userData.ProductID.Key, &productID)
if err != nil {
if err := util.GetSecretData(
ctx,
k8sClient,
secretNamespace,
in.UserData.ProductID.Name,
in.UserData.ProductID.Key,
&productID); err != nil {

return SecretData{}, err
}
}
Expand Down Expand Up @@ -84,16 +90,16 @@ func GetSecretResources(
}
}

if userData := in.UserData; userData != nil && userData.ProductID != nil {
if in.UserData.ProductID != nil {
s, err := util.GetSecretResource(
ctx,
k8sClient,
secretNamespace,
userData.ProductID.Name)
in.UserData.ProductID.Name)
if err != nil {
return nil, err
}
captureSecret(s, userData.ProductID.Name)
captureSecret(s, in.UserData.ProductID.Name)
}

if guiUnattended := in.GUIUnattended; guiUnattended != nil && guiUnattended.AutoLogon {
Expand Down
6 changes: 3 additions & 3 deletions pkg/providers/vsphere/sysprep/secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var _ = Describe("GetSysprepSecretData", func() {
productIDSecretName := "product_id_secret"
BeforeEach(func() {
inlineSysprep = vmopv1sysprep.Sysprep{
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
ProductID: &vmopv1sysprep.ProductIDSecretKeySelector{
Name: productIDSecretName,
Key: "product_id",
Expand Down Expand Up @@ -249,7 +249,7 @@ var _ = Describe("Sysprep GetSecretResources", func() {
productIDSecretName := "product_id_secret"
BeforeEach(func() {
inlineSysprep = vmopv1sysprep.Sysprep{
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
ProductID: &vmopv1sysprep.ProductIDSecretKeySelector{
Name: productIDSecretName,
Key: "product_id",
Expand Down Expand Up @@ -375,7 +375,7 @@ var _ = Describe("Sysprep GetSecretResources", func() {
secretName := "same-secret"
BeforeEach(func() {
inlineSysprep = vmopv1sysprep.Sysprep{
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
ProductID: &vmopv1sysprep.ProductIDSecretKeySelector{
Name: secretName,
Key: "product_id",
Expand Down
32 changes: 18 additions & 14 deletions pkg/providers/vsphere/vmlifecycle/bootstrap_sysprep.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,18 @@ func convertTo(from *vmopv1sysprep.Sysprep, bsArgs *BootstrapArgs) *vimtypes.Cus
bootstrapData := bsArgs.BootstrapData
sysprepCustomization := &vimtypes.CustomizationSysprep{}

if from.GUIUnattended != nil {
sysprepCustomization.GuiUnattended = vimtypes.CustomizationGuiUnattended{
TimeZone: from.GUIUnattended.TimeZone,
AutoLogon: from.GUIUnattended.AutoLogon,
AutoLogonCount: from.GUIUnattended.AutoLogonCount,
}
sysprepCustomization.GuiUnattended = vimtypes.CustomizationGuiUnattended{}

if from.GUIUnattended == nil {
// If spec.bootstrap.sysprep.guiUnattended is not set, then default
// the timezone to UTC, which is 85 per the Microsoft documentation at
// https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms912391(v=winembedded.11).
sysprepCustomization.GuiUnattended.TimeZone = 85
} else {
sysprepCustomization.GuiUnattended.TimeZone = from.GUIUnattended.TimeZone
sysprepCustomization.GuiUnattended.AutoLogon = from.GUIUnattended.AutoLogon
sysprepCustomization.GuiUnattended.AutoLogonCount = from.GUIUnattended.AutoLogonCount

if bootstrapData.Sysprep != nil && bootstrapData.Sysprep.Password != "" {
sysprepCustomization.GuiUnattended.Password = &vimtypes.CustomizationPassword{
Value: bootstrapData.Sysprep.Password,
Expand All @@ -111,14 +117,12 @@ func convertTo(from *vmopv1sysprep.Sysprep, bsArgs *BootstrapArgs) *vimtypes.Cus
Name: bsArgs.HostName,
},
}
if from.UserData != nil {
sysprepCustomization.UserData.FullName = from.UserData.FullName
sysprepCustomization.UserData.OrgName = from.UserData.OrgName
// In the case of a VMI with volume license key, this might not be set.
// Hence, add a check to see if the productID is set to empty.
if bootstrapData.Sysprep != nil && bootstrapData.Sysprep.ProductID != "" {
sysprepCustomization.UserData.ProductId = bootstrapData.Sysprep.ProductID
}
sysprepCustomization.UserData.FullName = from.UserData.FullName
sysprepCustomization.UserData.OrgName = from.UserData.OrgName
// In the case of a VMI with volume license key, this might not be set.
// Hence, add a check to see if the productID is set to empty.
if bootstrapData.Sysprep != nil && bootstrapData.Sysprep.ProductID != "" {
sysprepCustomization.UserData.ProductId = bootstrapData.Sysprep.ProductID
}

if from.GUIRunOnce != nil {
Expand Down
6 changes: 4 additions & 2 deletions pkg/providers/vsphere/vmlifecycle/bootstrap_sysprep_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ var _ = Describe("SysPrep Bootstrap", func() {
},
TimeZone: 4,
},
UserData: &vmopv1sysprep.UserData{
UserData: vmopv1sysprep.UserData{
FullName: "foo-bar",
OrgName: "foo-org",
ProductID: &vmopv1sysprep.ProductIDSecretKeySelector{Key: "product_id_key"},
Expand Down Expand Up @@ -188,7 +188,7 @@ var _ = Describe("SysPrep Bootstrap", func() {
bsArgs.Sysprep = nil
})

It("does not set", func() {
It("still sets some fields", func() {
Expect(err).ToNot(HaveOccurred())
Expect(custSpec).ToNot(BeNil())

Expand All @@ -198,6 +198,8 @@ var _ = Describe("SysPrep Bootstrap", func() {
name, ok := sysPrep.UserData.ComputerName.(*vimtypes.CustomizationFixedName)
Expect(ok).To(BeTrue())
Expect(name.Name).To(Equal(hostName))

Expect(sysPrep.GuiUnattended.TimeZone).To(Equal(int32(85)))
})
})
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/providers/vsphere/vmprovider_vm_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ func vmUtilTests() {
productIDSecretName := "product_id_secret"
BeforeEach(func() {
vmCtx.VM.Spec.Bootstrap.Sysprep.Sysprep = &sysprep.Sysprep{
UserData: &sysprep.UserData{
UserData: sysprep.UserData{
ProductID: &sysprep.ProductIDSecretKeySelector{
Name: productIDSecretName,
Key: "product_id",
Expand Down Expand Up @@ -1186,7 +1186,7 @@ func vmUtilTests() {
vmCtx.VM.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{
Sysprep: &vmopv1.VirtualMachineBootstrapSysprepSpec{
Sysprep: &sysprep.Sysprep{
UserData: &sysprep.UserData{
UserData: sysprep.UserData{
ProductID: &sysprep.ProductIDSecretKeySelector{
Name: "dummy-sysprep-secret",
},
Expand Down

0 comments on commit d107a82

Please sign in to comment.