Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ add VM naming strategy for VMs in govmomi mode #3286

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apis/v1alpha3/vspheremachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func (src *VSphereMachine) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.TagIDs = restored.Spec.TagIDs
dst.Spec.PowerOffMode = restored.Spec.PowerOffMode
dst.Spec.GuestSoftPowerOffTimeout = restored.Spec.GuestSoftPowerOffTimeout
dst.Spec.NamingStrategy = restored.Spec.NamingStrategy
for i := range dst.Spec.Network.Devices {
dst.Spec.Network.Devices[i].AddressesFromPools = restored.Spec.Network.Devices[i].AddressesFromPools
dst.Spec.Network.Devices[i].DHCP4Overrides = restored.Spec.Network.Devices[i].DHCP4Overrides
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha3/vspheremachinetemplate_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func (src *VSphereMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Template.Spec.AdditionalDisksGiB = restored.Spec.Template.Spec.AdditionalDisksGiB
dst.Spec.Template.Spec.PowerOffMode = restored.Spec.Template.Spec.PowerOffMode
dst.Spec.Template.Spec.GuestSoftPowerOffTimeout = restored.Spec.Template.Spec.GuestSoftPowerOffTimeout
dst.Spec.Template.Spec.NamingStrategy = restored.Spec.Template.Spec.NamingStrategy
for i := range dst.Spec.Template.Spec.Network.Devices {
dst.Spec.Template.Spec.Network.Devices[i].AddressesFromPools = restored.Spec.Template.Spec.Network.Devices[i].AddressesFromPools
dst.Spec.Template.Spec.Network.Devices[i].DHCP4Overrides = restored.Spec.Template.Spec.Network.Devices[i].DHCP4Overrides
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha3/zz_generated.conversion.go

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

1 change: 1 addition & 0 deletions apis/v1alpha4/vspheremachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func (src *VSphereMachine) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.TagIDs = restored.Spec.TagIDs
dst.Spec.PowerOffMode = restored.Spec.PowerOffMode
dst.Spec.GuestSoftPowerOffTimeout = restored.Spec.GuestSoftPowerOffTimeout
dst.Spec.NamingStrategy = restored.Spec.NamingStrategy
for i := range dst.Spec.Network.Devices {
dst.Spec.Network.Devices[i].AddressesFromPools = restored.Spec.Network.Devices[i].AddressesFromPools
dst.Spec.Network.Devices[i].DHCP4Overrides = restored.Spec.Network.Devices[i].DHCP4Overrides
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha4/vspheremachinetemplate_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func (src *VSphereMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Template.Spec.AdditionalDisksGiB = restored.Spec.Template.Spec.AdditionalDisksGiB
dst.Spec.Template.Spec.PowerOffMode = restored.Spec.Template.Spec.PowerOffMode
dst.Spec.Template.Spec.GuestSoftPowerOffTimeout = restored.Spec.Template.Spec.GuestSoftPowerOffTimeout
dst.Spec.Template.Spec.NamingStrategy = restored.Spec.Template.Spec.NamingStrategy
for i := range dst.Spec.Template.Spec.Network.Devices {
dst.Spec.Template.Spec.Network.Devices[i].AddressesFromPools = restored.Spec.Template.Spec.Network.Devices[i].AddressesFromPools
dst.Spec.Template.Spec.Network.Devices[i].DHCP4Overrides = restored.Spec.Template.Spec.Network.Devices[i].DHCP4Overrides
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha4/zz_generated.conversion.go

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

24 changes: 24 additions & 0 deletions apis/v1beta1/vspheremachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,30 @@ type VSphereMachineSpec struct {
//
// +optional
GuestSoftPowerOffTimeout *metav1.Duration `json:"guestSoftPowerOffTimeout,omitempty"`

// NamingStrategy allows configuring the naming strategy used when calculating the name of the VSphereVM.
Copy link
Member

@neolit123 neolit123 Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculating -> preparing
as it's not a math calculation.

EDIT: seems consistent with the supervisor PR. so you can leave it as is.

name of the VSphereVM.

this field is inside VSphereMachineSpec not VSphereVMSpec

// +optional
NamingStrategy *VSphereVMNamingStrategy `json:"namingStrategy,omitempty"`
}

// VSphereVMNamingStrategy defines the naming strategy for the VSphereVMs.
type VSphereVMNamingStrategy struct {
Comment on lines +77 to +78
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be named
VSphereMachineNamingStrategy

// Template defines the template to use for generating the name of the VSphereVM object.
// If not defined, it will fall back to `{{ .machine.name }}`.
// The templating has the following data available:
// * `.machine.name`: The name of the Machine object.
// The templating also has the following funcs available:
// * `trimSuffix`: same as strings.TrimSuffix
// * `trunc`: truncates a string, e.g. `trunc 2 "hello"` or `trunc -2 "hello"`
// Notes:
// * While the template offers some flexibility, we would like the name to link to the Machine name
// to ensure better user experience when troubleshooting
Comment on lines +87 to +88
Copy link
Member

@neolit123 neolit123 Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// * While the template offers some flexibility, we would like the name to link to the Machine name
// to ensure better user experience when troubleshooting
// * While the template offers some flexibility, it is best for the name to link to the Machine name
// to ensure better user experience when troubleshooting

personalization should not be present in official docs (ideally).

EDIT: seems consistent with the supervisor PR. so you can leave it as is.

// * Generated names must be valid Kubernetes names as they are used to create a VSphereVM object
// and usually also as the name of the Node object.
// * Names are automatically truncated at 63 characters. Please note that this can lead to name conflicts,
// so we highly recommend to use a template which leads to a name shorter than 63 characters.
// +optional
Template *string `json:"template,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for *string, just string?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: seems consistent with the supervisor PR. so you can leave it as is.

}

// VSphereMachineStatus defines the observed state of VSphereMachine.
Expand Down
25 changes: 25 additions & 0 deletions apis/v1beta1/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 @@ -1026,6 +1026,28 @@ spec:
virtual machine is cloned.
format: int64
type: integer
namingStrategy:
description: NamingStrategy allows configuring the naming strategy
used when calculating the name of the VSphereVM.
properties:
template:
description: |-
Template defines the template to use for generating the name of the VSphereVM object.
If not defined, it will fall back to `{{ .machine.name }}`.
The templating has the following data available:
* `.machine.name`: The name of the Machine object.
The templating also has the following funcs available:
* `trimSuffix`: same as strings.TrimSuffix
* `trunc`: truncates a string, e.g. `trunc 2 "hello"` or `trunc -2 "hello"`
Notes:
* While the template offers some flexibility, we would like the name to link to the Machine name
to ensure better user experience when troubleshooting
* Generated names must be valid Kubernetes names as they are used to create a VSphereVM object
and usually also as the name of the Node object.
* Names are automatically truncated at 63 characters. Please note that this can lead to name conflicts,
so we highly recommend to use a template which leads to a name shorter than 63 characters.
type: string
type: object
network:
description: Network is the network configuration for this machine's
VM.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,28 @@ spec:
virtual machine is cloned.
format: int64
type: integer
namingStrategy:
description: NamingStrategy allows configuring the naming
strategy used when calculating the name of the VSphereVM.
properties:
template:
description: |-
Template defines the template to use for generating the name of the VSphereVM object.
If not defined, it will fall back to `{{ .machine.name }}`.
The templating has the following data available:
* `.machine.name`: The name of the Machine object.
The templating also has the following funcs available:
* `trimSuffix`: same as strings.TrimSuffix
* `trunc`: truncates a string, e.g. `trunc 2 "hello"` or `trunc -2 "hello"`
Notes:
* While the template offers some flexibility, we would like the name to link to the Machine name
to ensure better user experience when troubleshooting
* Generated names must be valid Kubernetes names as they are used to create a VSphereVM object
and usually also as the name of the Node object.
* Names are automatically truncated at 63 characters. Please note that this can lead to name conflicts,
so we highly recommend to use a template which leads to a name shorter than 63 characters.
type: string
type: object
network:
description: Network is the network configuration for this
machine's VM.
Expand Down
43 changes: 42 additions & 1 deletion internal/webhooks/vspheremachinetemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import (

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/cluster-api/util/topology"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

infrav1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1"
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/services"
)

const machineTemplateImmutableMsg = "VSphereMachineTemplate spec.template.spec field is immutable. Please create a new resource instead."
Expand All @@ -50,7 +52,7 @@ func (webhook *VSphereMachineTemplateWebhook) SetupWebhookWithManager(mgr ctrl.M
}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
func (webhook *VSphereMachineTemplateWebhook) ValidateCreate(_ context.Context, raw runtime.Object) (admission.Warnings, error) {
func (webhook *VSphereMachineTemplateWebhook) ValidateCreate(ctx context.Context, raw runtime.Object) (admission.Warnings, error) {
obj, ok := raw.(*infrav1.VSphereMachineTemplate)
if !ok {
return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a VSphereMachineTemplate but got a %T", raw))
Expand Down Expand Up @@ -87,6 +89,10 @@ func (webhook *VSphereMachineTemplateWebhook) ValidateCreate(_ context.Context,
pciErrs := validatePCIDevices(spec.PciDevices)
allErrs = append(allErrs, pciErrs...)

templateErrs := validateVSphereVMNamingTemplate(ctx, obj)
if len(templateErrs) > 0 {
allErrs = append(allErrs, templateErrs...)
}
return nil, AggregateObjErrors(obj.GroupVersionKind().GroupKind(), obj.Name, allErrs)
}

Expand All @@ -111,10 +117,45 @@ func (webhook *VSphereMachineTemplateWebhook) ValidateUpdate(ctx context.Context
!reflect.DeepEqual(newTyped.Spec.Template.Spec, oldTyped.Spec.Template.Spec) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "template", "spec"), newTyped, machineTemplateImmutableMsg))
}

templateErrs := validateVSphereVMNamingTemplate(ctx, newTyped)
if len(templateErrs) > 0 {
allErrs = append(allErrs, templateErrs...)
}
return nil, AggregateObjErrors(newTyped.GroupVersionKind().GroupKind(), newTyped.Name, allErrs)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.
func (webhook *VSphereMachineTemplateWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) {
return nil, nil
}

func validateVSphereVMNamingTemplate(_ context.Context, vsphereMachineTemplate *infrav1.VSphereMachineTemplate) field.ErrorList {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this following some code that core CAPI already has or is this done from scratch as a use case for CAPV?
if the former, could you link to that code for reviewer's reference?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is based on this MR which was added to support this in supervisor mode #3099

In supervisor mode, the object is called VirtualMachine which drives the creation of vSphere VMs while in govami mode this object is called VSphereVM.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like a lot comes from that PR.
why not also name it VirtualMachineNamingStrategy here? that seems more consistent despite the diff backend objects.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do that if we prefer that. I just to wanted to call it what it is being used for.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would be my preference. not sure what other reviewers will comment on this.

var allErrs field.ErrorList
namingStrategy := vsphereMachineTemplate.Spec.Template.Spec.NamingStrategy
if namingStrategy != nil && namingStrategy.Template != nil {
name, err := services.GenerateVSphereVMName("machine", namingStrategy)
templateFldPath := field.NewPath("spec", "template", "spec", "namingStrategy", "template")
if err != nil {
allErrs = append(allErrs,
field.Invalid(
templateFldPath,
*namingStrategy.Template,
fmt.Sprintf("invalid VSphereVM name template: %v", err),
),
)
} else {
// Note: This validates that the resulting name is a valid Kubernetes object name.
for _, err := range validation.IsDNS1123Subdomain(name) {
allErrs = append(allErrs,
field.Invalid(
templateFldPath,
*namingStrategy.Template,
fmt.Sprintf("invalid VSphereVM name template, generated name is not a valid Kubernetes object name: %v", err),
),
)
}
}
}
return allErrs
}
Loading
Loading