From 17fb955e822cef5f5ee5b0ec02033ce27ab05167 Mon Sep 17 00:00:00 2001 From: vr4manta Date: Mon, 17 Nov 2025 07:19:24 -0500 Subject: [PATCH 1/2] Vendor updates --- go.mod | 2 +- go.sum | 4 +- .../openshift/api/features/features.go | 4 +- .../api/machine/v1beta1/types_awsprovider.go | 51 +++++++++++++++++++ .../machine/v1beta1/zz_generated.deepcopy.go | 47 +++++++++++++++++ .../zz_generated.swagger_doc_generated.go | 20 ++++++++ vendor/modules.txt | 2 +- 7 files changed, 124 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a60dbd402..d092d310d 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/onsi/ginkgo/v2 v2.27.2 github.com/onsi/gomega v1.38.2 github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7 - github.com/openshift/api v0.0.0-20251106190826-ebe535b08719 + github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7 github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235 github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20251029084908-344babe6a957 diff --git a/go.sum b/go.sum index f8179f820..fd132c885 100644 --- a/go.sum +++ b/go.sum @@ -449,8 +449,8 @@ github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jD github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7 h1:Z1swlS6b3Adm6RPhjqefs3DWnNFLDxRX+WC8GMXhja4= github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M= -github.com/openshift/api v0.0.0-20251106190826-ebe535b08719 h1:KEwYyKaJniwhoyLB75tAMmJn9pMlk0PUlRfrsXYOhwM= -github.com/openshift/api v0.0.0-20251106190826-ebe535b08719/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= +github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7 h1:MemawsK6SpxEaE5y0NqO5sIX3yTLIIyP89w6DGKukAk= +github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235 h1:9JBeIXmnHlpXTQPi7LPmu1jdxznBhAE7bb1K+3D8gxY= github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235/go.mod h1:L49W6pfrZkfOE5iC1PqEkuLkXG4W0BX4w8b+L2Bv7fM= github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d h1:+sqUThLi/lmgT5/scmmjnS6+RZFtbdxRAscNfCPyLPI= diff --git a/vendor/github.com/openshift/api/features/features.go b/vendor/github.com/openshift/api/features/features.go index 910cabc5f..33e51639c 100644 --- a/vendor/github.com/openshift/api/features/features.go +++ b/vendor/github.com/openshift/api/features/features.go @@ -760,8 +760,8 @@ var ( mustRegister() FeatureGateAWSDedicatedHosts = newFeatureGate("AWSDedicatedHosts"). - reportProblemsToJiraComponent("Installer"). - contactPerson("faermanj"). + reportProblemsToJiraComponent("splat"). + contactPerson("rvanderp3"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1781"). enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). diff --git a/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go b/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go index b3b38bc6c..161f2324c 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go @@ -114,6 +114,14 @@ type AWSMachineProviderConfig struct { // If this value is selected, capacityReservationID must be specified to identify the target reservation. // +optional MarketType MarketType `json:"marketType,omitempty"` + + // hostPlacement configures placement on AWS Dedicated Hosts. This allows admins to assign instances to specific host + // for a variety of needs including for regulatory compliance, to leverage existing per-socket or per-core software licenses (BYOL), + // and to gain visibility and control over instance placement on a physical server. + // When omitted, the instance is not constrained to a dedicated host. + // +openshift:enable:FeatureGate=AWSDedicatedHosts + // +optional + HostPlacement *HostPlacement `json:"hostPlacement,omitempty"` } // AWSConfidentialComputePolicy represents the confidential compute configuration for the instance. @@ -393,3 +401,46 @@ const ( // When set to CapacityBlock the instance utilizes pre-purchased compute capacity (capacity blocks) with AWS Capacity Reservations. MarketTypeCapacityBlock MarketType = "CapacityBlock" ) + +// HostPlacement is the type that will be used to configure the placement of AWS instances. +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.affinity == 'DedicatedHost' ? has(self.dedicatedHost) : !has(self.dedicatedHost)",message="dedicatedHost is required when affinity is DedicatedHost, and forbidden otherwise" +// +union +type HostPlacement struct { + // affinity specifies the affinity setting for the instance. + // Allowed values are AnyAvailable and DedicatedHost. + // When Affinity is set to DedicatedHost, an instance started onto a specific host always restarts on the same host if stopped. In this scenario, the `dedicatedHost` field must be set. + // When Affinity is set to AnyAvailable, and you stop and restart the instance, it can be restarted on any available host. + // +required + // +unionDiscriminator + Affinity *HostAffinity `json:"affinity,omitempty"` + + // dedicatedHost specifies the exact host that an instance should be restarted on if stopped. + // dedicatedHost is required when 'affinity' is set to DedicatedHost, and forbidden otherwise. + // +optional + // +unionMember + DedicatedHost *DedicatedHost `json:"dedicatedHost,omitempty"` +} + +// HostAffinity selects how an instance should be placed on AWS Dedicated Hosts. +// +kubebuilder:validation:Enum:=DedicatedHost;AnyAvailable +type HostAffinity string + +const ( + // HostAffinityAnyAvailable lets the platform select any available dedicated host. + HostAffinityAnyAvailable HostAffinity = "AnyAvailable" + + // HostAffinityDedicatedHost requires specifying a particular host via dedicatedHost.host.hostID. + HostAffinityDedicatedHost HostAffinity = "DedicatedHost" +) + +// DedicatedHost represents the configuration for the usage of dedicated host. +type DedicatedHost struct { + // id identifies the AWS Dedicated Host on which the instance must run. + // The value must start with "h-" followed by 17 lowercase hexadecimal characters (0-9 and a-f). + // Must be exactly 19 characters in length. + // +kubebuilder:validation:XValidation:rule="self.matches('^h-[0-9a-f]{17}$')",message="hostID must start with 'h-' followed by 17 lowercase hexadecimal characters (0-9 and a-f)" + // +kubebuilder:validation:MinLength=19 + // +kubebuilder:validation:MaxLength=19 + // +required + ID string `json:"id,omitempty"` +} diff --git a/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go index 554fc19b9..92586dcdf 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go @@ -85,6 +85,11 @@ func (in *AWSMachineProviderConfig) DeepCopyInto(out *AWSMachineProviderConfig) *out = new(int32) **out = **in } + if in.HostPlacement != nil { + in, out := &in.HostPlacement, &out.HostPlacement + *out = new(HostPlacement) + (*in).DeepCopyInto(*out) + } return } @@ -509,6 +514,22 @@ func (in *DataDiskManagedDiskParameters) DeepCopy() *DataDiskManagedDiskParamete return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DedicatedHost) DeepCopyInto(out *DedicatedHost) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DedicatedHost. +func (in *DedicatedHost) DeepCopy() *DedicatedHost { + if in == nil { + return nil + } + out := new(DedicatedHost) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiskEncryptionSetParameters) DeepCopyInto(out *DiskEncryptionSetParameters) { *out = *in @@ -903,6 +924,32 @@ func (in *GCPShieldedInstanceConfig) DeepCopy() *GCPShieldedInstanceConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostPlacement) DeepCopyInto(out *HostPlacement) { + *out = *in + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(HostAffinity) + **out = **in + } + if in.DedicatedHost != nil { + in, out := &in.DedicatedHost, &out.DedicatedHost + *out = new(DedicatedHost) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPlacement. +func (in *HostPlacement) DeepCopy() *HostPlacement { + if in == nil { + return nil + } + out := new(HostPlacement) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Image) DeepCopyInto(out *Image) { *out = *in diff --git a/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go index 7b74d37d0..23b6eddd3 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go @@ -35,6 +35,7 @@ var map_AWSMachineProviderConfig = map[string]string{ "placementGroupPartition": "placementGroupPartition is the partition number within the placement group in which to launch the instance. This must be an integer value between 1 and 7. It is only valid if the placement group, referred in `PlacementGroupName` was created with strategy set to partition.", "capacityReservationId": "capacityReservationId specifies the target Capacity Reservation into which the instance should be launched. The field size should be greater than 0 and the field input must start with cr-***", "marketType": "marketType specifies the type of market for the EC2 instance. Valid values are OnDemand, Spot, CapacityBlock and omitted.\n\nDefaults to OnDemand. When SpotMarketOptions is provided, the marketType defaults to \"Spot\".\n\nWhen set to OnDemand the instance runs as a standard OnDemand instance. When set to Spot the instance runs as a Spot instance. When set to CapacityBlock the instance utilizes pre-purchased compute capacity (capacity blocks) with AWS Capacity Reservations. If this value is selected, capacityReservationID must be specified to identify the target reservation.", + "hostPlacement": "hostPlacement configures placement on AWS Dedicated Hosts. This allows admins to assign instances to specific host for a variety of needs including for regulatory compliance, to leverage existing per-socket or per-core software licenses (BYOL), and to gain visibility and control over instance placement on a physical server. When omitted, the instance is not constrained to a dedicated host.", } func (AWSMachineProviderConfig) SwaggerDoc() map[string]string { @@ -92,6 +93,15 @@ func (CPUOptions) SwaggerDoc() map[string]string { return map_CPUOptions } +var map_DedicatedHost = map[string]string{ + "": "DedicatedHost represents the configuration for the usage of dedicated host.", + "id": "id identifies the AWS Dedicated Host on which the instance must run. The value must start with \"h-\" followed by 17 lowercase hexadecimal characters (0-9 and a-f). Must be exactly 19 characters in length.", +} + +func (DedicatedHost) SwaggerDoc() map[string]string { + return map_DedicatedHost +} + var map_EBSBlockDeviceSpec = map[string]string{ "": "EBSBlockDeviceSpec describes a block device for an EBS volume. https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/EbsBlockDevice", "deleteOnTermination": "Indicates whether the EBS volume is deleted on machine termination.\n\nDeprecated: setting this field has no effect.", @@ -116,6 +126,16 @@ func (Filter) SwaggerDoc() map[string]string { return map_Filter } +var map_HostPlacement = map[string]string{ + "": "HostPlacement is the type that will be used to configure the placement of AWS instances.", + "affinity": "affinity specifies the affinity setting for the instance. Allowed values are AnyAvailable and DedicatedHost. When Affinity is set to DedicatedHost, an instance started onto a specific host always restarts on the same host if stopped. In this scenario, the `dedicatedHost` field must be set. When Affinity is set to AnyAvailable, and you stop and restart the instance, it can be restarted on any available host.", + "dedicatedHost": "dedicatedHost specifies the exact host that an instance should be restarted on if stopped. dedicatedHost is required when 'affinity' is set to DedicatedHost, and forbidden otherwise.", +} + +func (HostPlacement) SwaggerDoc() map[string]string { + return map_HostPlacement +} + var map_LoadBalancerReference = map[string]string{ "": "LoadBalancerReference is a reference to a load balancer on AWS.", } diff --git a/vendor/modules.txt b/vendor/modules.txt index 3a4c37e38..658a9d7a5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -976,7 +976,7 @@ github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo github.com/openshift-eng/openshift-tests-extension/pkg/junit github.com/openshift-eng/openshift-tests-extension/pkg/util/sets github.com/openshift-eng/openshift-tests-extension/pkg/version -# github.com/openshift/api v0.0.0-20251106190826-ebe535b08719 +# github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7 ## explicit; go 1.24.0 github.com/openshift/api github.com/openshift/api/annotations From 7c6c27f93a2496c718955450ff2ea2c1a4881f57 Mon Sep 17 00:00:00 2001 From: vr4manta Date: Mon, 17 Nov 2025 07:19:46 -0500 Subject: [PATCH 2/2] AWS dedicated host webhook updates --- pkg/operator/operator.go | 1 + pkg/operator/operator_test.go | 2 + pkg/webhooks/machine_webhook.go | 37 ++++++++ pkg/webhooks/machine_webhook_test.go | 135 +++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 58bc56ba4..8672165d9 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -472,6 +472,7 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) { // flags, we selectively populate the map (and therefore passed // as args) features := map[string]bool{ + string(apifeatures.FeatureGateAWSDedicatedHosts): featureGates.Enabled(apifeatures.FeatureGateAWSDedicatedHosts), string(apifeatures.FeatureGateMachineAPIMigration): featureGates.Enabled(apifeatures.FeatureGateMachineAPIMigration), string(apifeatures.FeatureGateAzureWorkloadIdentity): featureGates.Enabled(apifeatures.FeatureGateAzureWorkloadIdentity), string(apifeatures.FeatureGateVSphereMultiDisk): featureGates.Enabled(apifeatures.FeatureGateVSphereMultiDisk), diff --git a/pkg/operator/operator_test.go b/pkg/operator/operator_test.go index c6e28b72e..8188a8999 100644 --- a/pkg/operator/operator_test.go +++ b/pkg/operator/operator_test.go @@ -45,6 +45,7 @@ var ( {Name: apifeatures.FeatureGateAzureWorkloadIdentity}, {Name: apifeatures.FeatureGateVSphereMultiDisk}, {Name: apifeatures.FeatureGateVSphereHostVMGroupZonal}, + {Name: apifeatures.FeatureGateAWSDedicatedHosts}, } enabledFeatureMap = map[string]bool{ @@ -52,6 +53,7 @@ var ( "AzureWorkloadIdentity": true, "VSphereMultiDisk": true, "VSphereHostVMGroupZonal": true, + "AWSDedicatedHosts": true, } ) diff --git a/pkg/webhooks/machine_webhook.go b/pkg/webhooks/machine_webhook.go index 3b5d491c9..026cbb0b2 100644 --- a/pkg/webhooks/machine_webhook.go +++ b/pkg/webhooks/machine_webhook.go @@ -50,6 +50,11 @@ type systemSpecifications struct { type machineArch string var ( + // AWS Variables / Defaults + + // awsDedicatedHostNamePattern is used to validate the id of a dedicated host + awsDedicatedHostNamePattern = regexp.MustCompile(`^h-[0-9a-f]{17}$`) + // Azure Defaults defaultAzureVnet = func(clusterID string) string { return fmt.Sprintf("%s-vnet", clusterID) @@ -897,6 +902,38 @@ func validateAWS(m *machinev1beta1.Machine, config *admissionConfig) (bool, []st } } + // Dedicated host support. + // Check if host placement is configured. If so, then we need to determine placement affinity and validate configs. + if providerSpec.HostPlacement != nil { + klog.V(4).Infof("Validating AWS Host Placement") + placement := *providerSpec.HostPlacement + if placement.Affinity == nil { + errs = append(errs, field.Required(field.NewPath("spec.hostPlacement.affinity"), "affinity is required and must be set to either AnyAvailable or DedicatedHost")) + } else { + switch *placement.Affinity { + case machinev1beta1.HostAffinityAnyAvailable: + // Cannot have DedicatedHost set + if placement.DedicatedHost != nil { + errs = append(errs, field.Forbidden(field.NewPath("spec.hostPlacement.dedicatedHost"), "dedicatedHost is required when affinity is DedicatedHost, and forbidden otherwise")) + } + case machinev1beta1.HostAffinityDedicatedHost: + // We need to make sure DedicatedHost is set with a HostID + if placement.DedicatedHost == nil { + errs = append(errs, field.Required(field.NewPath("spec.hostPlacement.dedicatedHost"), "dedicatedHost is required when affinity is DedicatedHost, and forbidden otherwise")) + } else { + // If not set, return required error. If it does not match pattern, return pattern failure message. + if placement.DedicatedHost.ID == "" { + errs = append(errs, field.Required(field.NewPath("spec.hostPlacement.dedicatedHost.id"), "id is required and must start with 'h-' followed by 17 lowercase hexadecimal characters (0-9 and a-f)")) + } else if awsDedicatedHostNamePattern.FindStringSubmatch(placement.DedicatedHost.ID) == nil { + errs = append(errs, field.Invalid(field.NewPath("spec.hostPlacement.dedicatedHost.id"), placement.DedicatedHost.ID, "id must start with 'h-' followed by 17 lowercase hexadecimal characters (0-9 and a-f)")) + } + } + default: + errs = append(errs, field.Invalid(field.NewPath("spec.hostPlacement.affinity"), placement.Affinity, "affinity must be either AnyAvailable or DedicatedHost")) + } + } + } + if len(errs) > 0 { return false, warnings, errs } diff --git a/pkg/webhooks/machine_webhook_test.go b/pkg/webhooks/machine_webhook_test.go index 56ab60b85..2741451c7 100644 --- a/pkg/webhooks/machine_webhook_test.go +++ b/pkg/webhooks/machine_webhook_test.go @@ -316,6 +316,141 @@ func TestMachineCreation(t *testing.T) { }, expectedError: "", }, + { + name: "configure host placement with AnyAvailable affinity", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ + ID: ptr.To[string]("ami"), + }, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityAnyAvailable), + }, + }, + }, + expectedError: "", + }, + { + name: "configure host placement with invalid affinity", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ + ID: ptr.To[string]("ami"), + }, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinity("invalid")), + }, + }, + }, + expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.affinity: Invalid value: \"invalid\": affinity must be either AnyAvailable or DedicatedHost", + }, + { + name: "configure host placement dedicatedHost without dedicatedHost", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ + ID: ptr.To[string]("ami"), + }, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost), + }, + }, + }, + expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.dedicatedHost: Required value: dedicatedHost is required when affinity is DedicatedHost", + }, + { + name: "configure host placement dedicatedHost with valid ID", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ + ID: ptr.To[string]("ami"), + }, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost), + DedicatedHost: &machinev1beta1.DedicatedHost{ID: "h-1234567890abcdef0"}, + }, + }, + }, + expectedError: "", + }, + { + name: "configure host placement AnyAvailable forbids dedicatedHost", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")}, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityAnyAvailable), + DedicatedHost: &machinev1beta1.DedicatedHost{ID: "h-09dcf61cb388b0149"}, + }, + }, + }, + expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.dedicatedHost: Forbidden: dedicatedHost is required when affinity is DedicatedHost, and forbidden otherwise", + }, + { + name: "configure host placement dedicatedHost with empty ID", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")}, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost), + DedicatedHost: &machinev1beta1.DedicatedHost{ID: ""}, + }, + }, + }, + expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.dedicatedHost.id: Required value: id is required and must start with 'h-' followed by 17 lowercase hexadecimal characters (0-9 and a-f)", + }, + { + name: "configure host placement dedicatedHost with ID not set", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")}, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost), + DedicatedHost: &machinev1beta1.DedicatedHost{}, + }, + }, + }, + expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.dedicatedHost.id: Required value: id is required and must start with 'h-' followed by 17 lowercase hexadecimal characters (0-9 and a-f)", + }, + { + name: "configure host placement dedicatedHost with invalid ID", + platformType: osconfigv1.AWSPlatformType, + clusterID: "aws-cluster", + providerSpecValue: &kruntime.RawExtension{ + Object: &machinev1beta1.AWSMachineProviderConfig{ + AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")}, + InstanceType: "test", + HostPlacement: &machinev1beta1.HostPlacement{ + Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost), + DedicatedHost: &machinev1beta1.DedicatedHost{ + ID: "invalid", + }, + }, + }, + }, + expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.dedicatedHost.id: Invalid value: \"invalid\": id must start with 'h-' followed by 17 lowercase hexadecimal characters (0-9 and a-f)", + }, { name: "with Azure and a nil provider spec value", platformType: osconfigv1.AzurePlatformType,