Skip to content

Commit

Permalink
Adds inline sysprep VM bootstrap support
Browse files Browse the repository at this point in the history
This patch adds support for inline sysprep which was introduced in the
API in v1alpha2. For the secrets, we are currently decoding them and
adding the values SysprepSecretData field under BootstrapArgs.

This patch also adds tests for the changes to enable inline sysprep.

Instead of waiting till doBootstrap to pull the secret data for inline
sysprep, this patch adds the secret data to the BootstrapArgs earlier
for verification purposes.

It updates the API to mark certain fields as mandatory.

This patch moves the defaulting logic of the secret selector keys to the
CRDs instead of relying on a mutating webhook

Signed-off-by: Sagar Muchhal <[email protected]>
  • Loading branch information
srm09 committed Dec 18, 2023
1 parent c061c1f commit ed86d24
Show file tree
Hide file tree
Showing 22 changed files with 990 additions and 105 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,6 @@ issues:
text: ".* `ctx` is unused"
- path: pkg/vmprovider/providers/vsphere/internal/internal.go
text: ".*ST1003|don\'t use underscores in Go names.*"
- path: _test.go
linters:
- gosec
69 changes: 50 additions & 19 deletions api/v1alpha2/sysprep/sysprep.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@

package sysprep

import (
"github.com/vmware-tanzu/vm-operator/api/v1alpha2/common"
)

// Sysprep describes the object representation of a Windows sysprep.xml answer
// file.
//
Expand All @@ -25,10 +21,10 @@ type Sysprep struct {
GUIRunOnce GUIRunOnce `json:"guiRunOnce,omitempty"`

// GUIUnattended is a representation of the Sysprep GUIUnattended key.
GUIUnattended GUIUnattended `json:"guiUnattended"`
GUIUnattended *GUIUnattended `json:"guiUnattended,omitempty"`

// Identification is a representation of the Sysprep Identification key.
Identification Identification `json:"identification"`
Identification *Identification `json:"identification,omitempty"`

// LicenseFilePrintData is a representation of the Sysprep
// LicenseFilePrintData key.
Expand All @@ -40,7 +36,7 @@ type Sysprep struct {
LicenseFilePrintData *LicenseFilePrintData `json:"licenseFilePrintData,omitempty"`

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

// GUIRunOnce maps to the GuiRunOnce key in the sysprep.xml answer file.
Expand All @@ -55,10 +51,10 @@ type GUIRunOnce struct {
// GUIUnattended maps to the GuiUnattended key in the sysprep.xml answer file.
type GUIUnattended struct {

// AutoLogon determine whether or not the machine automatically logs on as
// AutoLogon determine whether the machine automatically logs on as
// Administrator.
//
// Please note if AutoLogin is true, then Password must be set or guest
// Please note if AutoLogon is true, then Password must be set or guest
// customization will fail.
//
// +optional
Expand All @@ -71,7 +67,7 @@ type GUIUnattended struct {
// you may want to increase it. This number may be determined by the list of
// commands executed by the GuiRunOnce command.
//
// Please note this field only matters if AutoLogin is true.
// Please note this field only matters if AutoLogon is true.
//
// +optional
AutoLogonCount int32 `json:"autoLogonCount,omitempty"`
Expand All @@ -90,8 +86,11 @@ type GUIUnattended struct {
// plainText attribute to true, so that the customization process does not
// attempt to decrypt the string.
//
// When not explicitly specified, the Key field for the selector defaults to
// `password`.
//
// +optional
Password *common.SecretKeySelector `json:"password,omitempty"`
Password *PasswordSecretKeySelector `json:"password,omitempty"`

// TimeZone is the time zone index for the virtual machine.
//
Expand All @@ -102,6 +101,16 @@ type GUIUnattended struct {
TimeZone int32 `json:"timeZone,omitempty"`
}

// PasswordSecretKeySelector references the password value from a Secret resource
type PasswordSecretKeySelector struct {
// Name is the name of the secret.
Name string `json:"name"`

// Key is the key in the secret that specifies the requested data.
// +kubebuilder:default=password
Key string `json:"key"`
}

// Identification maps to the Identification key in the sysprep.xml answer file
// and provides information needed to join a workgroup or domain.
type Identification struct {
Expand All @@ -117,8 +126,11 @@ type Identification struct {
// DomainAdminPassword is the password for the domain user account used for
// authentication if the virtual machine is joining a domain.
//
// When not explicitly specified, the Key field for the selector defaults to
// `domain_admin_password`.
//
// +optional
DomainAdminPassword *common.SecretKeySelector `json:"domainAdminPassword,omitempty"`
DomainAdminPassword *DomainPasswordSecretKeySelector `json:"domainAdminPassword,omitempty"`

// JoinDomain is the domain that the virtual machine should join. If this
// value is supplied, then DomainAdmin and DomainAdminPassword must also be
Expand All @@ -135,6 +147,16 @@ type Identification struct {
JoinWorkgroup string `json:"joinWorkgroup,omitempty"`
}

// DomainPasswordSecretKeySelector references the password value from a Secret resource
type DomainPasswordSecretKeySelector struct {
// Name is the name of the secret.
Name string `json:"name"`

// Key is the key in the secret that specifies the requested data.
// +kubebuilder:default=domain_admin_password
Key string `json:"key"`
}

// CustomizationLicenseDataMode is an enumeration of the different license
// modes.
//
Expand Down Expand Up @@ -174,20 +196,29 @@ type LicenseFilePrintData struct {
type UserData struct {

// FullName is the user's full name.
//
// +optional
FullName string `json:"fullName,omitempty"`
FullName string `json:"fullName"`

// OrgName is the name of the user's organization.
//
// +optional
OrgName string `json:"orgName,omitempty"`
OrgName string `json:"orgName"`

// ProductID is a valid serial number.
//
// Please note unless the VirtualMachineImage was installed with a volume
// license key, ProductID must be set or guest customization will fail.
//
// When not explicitly specified, the Key field for the selector defaults to
// `domain_admin_password`.
//
// +optional
ProductID *common.SecretKeySelector `json:"productID,omitempty"`
ProductID *ProductIDSecretKeySelector `json:"productID,omitempty"`
}

// ProductIDSecretKeySelector references the ProductID value from a Secret resource
type ProductIDSecretKeySelector struct {
// Name is the name of the secret.
Name string `json:"name"`

// Key is the key in the secret that specifies the requested data.
// +kubebuilder:default=product_id
Key string `json:"key"`
}
73 changes: 64 additions & 9 deletions api/v1alpha2/sysprep/zz_generated.deepcopy.go

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

34 changes: 22 additions & 12 deletions config/crd/bases/vmoperator.vmware.com_virtualmachines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1088,10 +1088,10 @@ spec:
Sysprep GUIUnattended key.
properties:
autoLogon:
description: "AutoLogon determine whether or not the
machine automatically logs on as Administrator.
\n Please note if AutoLogin is true, then Password
must be set or guest customization will fail."
description: "AutoLogon determine whether the machine
automatically logs on as Administrator. \n Please
note if AutoLogon is true, then Password must be
set or guest customization will fail."
type: boolean
autoLogonCount:
description: "AutoLogonCount specifies the number
Expand All @@ -1101,7 +1101,7 @@ spec:
may want to increase it. This number may be determined
by the list of commands executed by the GuiRunOnce
command. \n Please note this field only matters
if AutoLogin is true."
if AutoLogon is true."
format: int32
type: integer
password:
Expand All @@ -1116,9 +1116,12 @@ spec:
Wizard, then the password is encrypted. Otherwise,
the client should set the plainText attribute to
true, so that the customization process does not
attempt to decrypt the string."
attempt to decrypt the string. \n When not explicitly
specified, the Key field for the selector defaults
to `password`."
properties:
key:
default: password
description: Key is the key in the secret that
specifies the requested data.
type: string
Expand Down Expand Up @@ -1149,11 +1152,14 @@ spec:
domain.
type: string
domainAdminPassword:
description: DomainAdminPassword is the password for
the domain user account used for authentication
if the virtual machine is joining a domain.
description: "DomainAdminPassword is the password
for the domain user account used for authentication
if the virtual machine is joining a domain. \n When
not explicitly specified, the Key field for the
selector defaults to `domain_admin_password`."
properties:
key:
default: domain_admin_password
description: Key is the key in the secret that
specifies the requested data.
type: string
Expand Down Expand Up @@ -1215,9 +1221,12 @@ spec:
description: "ProductID is a valid serial number.
\n Please note unless the VirtualMachineImage was
installed with a volume license key, ProductID must
be set or guest customization will fail."
be set or guest customization will fail. \n When
not explicitly specified, the Key field for the
selector defaults to `domain_admin_password`."
properties:
key:
default: product_id
description: Key is the key in the secret that
specifies the requested data.
type: string
Expand All @@ -1228,10 +1237,11 @@ spec:
- key
- name
type: object
required:
- fullName
- orgName
type: object
required:
- guiUnattended
- identification
- userData
type: object
type: object
Expand Down
7 changes: 4 additions & 3 deletions pkg/util/cloudinit/cloudconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/vmware-tanzu/vm-operator/api/v1alpha2/cloudinit"
"github.com/vmware-tanzu/vm-operator/api/v1alpha2/common"

"github.com/vmware-tanzu/vm-operator/pkg/util"
"github.com/vmware-tanzu/vm-operator/pkg/util/cloudinit/validate"
)

Expand Down Expand Up @@ -282,7 +283,7 @@ func GetSecretResources(

for i := range in.Users {
if v := in.Users[i].HashedPasswd; v != nil {
s, err := getSecretResource(
s, err := util.GetSecretResource(
ctx,
k8sClient,
secretNamespace,
Expand All @@ -293,7 +294,7 @@ func GetSecretResources(
captureSecret(s, v.Name)
}
if v := in.Users[i].Passwd; v != nil {
s, err := getSecretResource(
s, err := util.GetSecretResource(
ctx,
k8sClient,
secretNamespace,
Expand All @@ -309,7 +310,7 @@ func GetSecretResources(
if v := in.WriteFiles[i].Content; len(v) > 0 {
var sks common.SecretKeySelector
if err := yaml.Unmarshal(v, &sks); err == nil {
s, err := getSecretResource(
s, err := util.GetSecretResource(
ctx,
k8sClient,
secretNamespace,
Expand Down
Loading

0 comments on commit ed86d24

Please sign in to comment.