Skip to content

Commit 89954cf

Browse files
Merge pull request #1436 from vr4manta/SPLAT-2167_2
SPLAT-2167: Added logic to webhook for AWS dedicated host support
2 parents 026e9df + 7c6c27f commit 89954cf

File tree

11 files changed

+299
-6
lines changed

11 files changed

+299
-6
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/onsi/ginkgo/v2 v2.27.2
2020
github.com/onsi/gomega v1.38.2
2121
github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7
22-
github.com/openshift/api v0.0.0-20251106190826-ebe535b08719
22+
github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7
2323
github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235
2424
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d
2525
github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20251029084908-344babe6a957

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jD
449449
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
450450
github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7 h1:Z1swlS6b3Adm6RPhjqefs3DWnNFLDxRX+WC8GMXhja4=
451451
github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M=
452-
github.com/openshift/api v0.0.0-20251106190826-ebe535b08719 h1:KEwYyKaJniwhoyLB75tAMmJn9pMlk0PUlRfrsXYOhwM=
453-
github.com/openshift/api v0.0.0-20251106190826-ebe535b08719/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY=
452+
github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7 h1:MemawsK6SpxEaE5y0NqO5sIX3yTLIIyP89w6DGKukAk=
453+
github.com/openshift/api v0.0.0-20251111193948-50e2ece149d7/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY=
454454
github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235 h1:9JBeIXmnHlpXTQPi7LPmu1jdxznBhAE7bb1K+3D8gxY=
455455
github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235/go.mod h1:L49W6pfrZkfOE5iC1PqEkuLkXG4W0BX4w8b+L2Bv7fM=
456456
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d h1:+sqUThLi/lmgT5/scmmjnS6+RZFtbdxRAscNfCPyLPI=

pkg/operator/operator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) {
472472
// flags, we selectively populate the map (and therefore passed
473473
// as args)
474474
features := map[string]bool{
475+
string(apifeatures.FeatureGateAWSDedicatedHosts): featureGates.Enabled(apifeatures.FeatureGateAWSDedicatedHosts),
475476
string(apifeatures.FeatureGateMachineAPIMigration): featureGates.Enabled(apifeatures.FeatureGateMachineAPIMigration),
476477
string(apifeatures.FeatureGateAzureWorkloadIdentity): featureGates.Enabled(apifeatures.FeatureGateAzureWorkloadIdentity),
477478
string(apifeatures.FeatureGateVSphereMultiDisk): featureGates.Enabled(apifeatures.FeatureGateVSphereMultiDisk),

pkg/operator/operator_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ var (
4545
{Name: apifeatures.FeatureGateAzureWorkloadIdentity},
4646
{Name: apifeatures.FeatureGateVSphereMultiDisk},
4747
{Name: apifeatures.FeatureGateVSphereHostVMGroupZonal},
48+
{Name: apifeatures.FeatureGateAWSDedicatedHosts},
4849
}
4950

5051
enabledFeatureMap = map[string]bool{
5152
"MachineAPIMigration": true,
5253
"AzureWorkloadIdentity": true,
5354
"VSphereMultiDisk": true,
5455
"VSphereHostVMGroupZonal": true,
56+
"AWSDedicatedHosts": true,
5557
}
5658
)
5759

pkg/webhooks/machine_webhook.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ type systemSpecifications struct {
5050
type machineArch string
5151

5252
var (
53+
// AWS Variables / Defaults
54+
55+
// awsDedicatedHostNamePattern is used to validate the id of a dedicated host
56+
awsDedicatedHostNamePattern = regexp.MustCompile(`^h-[0-9a-f]{17}$`)
57+
5358
// Azure Defaults
5459
defaultAzureVnet = func(clusterID string) string {
5560
return fmt.Sprintf("%s-vnet", clusterID)
@@ -897,6 +902,38 @@ func validateAWS(m *machinev1beta1.Machine, config *admissionConfig) (bool, []st
897902
}
898903
}
899904

905+
// Dedicated host support.
906+
// Check if host placement is configured. If so, then we need to determine placement affinity and validate configs.
907+
if providerSpec.HostPlacement != nil {
908+
klog.V(4).Infof("Validating AWS Host Placement")
909+
placement := *providerSpec.HostPlacement
910+
if placement.Affinity == nil {
911+
errs = append(errs, field.Required(field.NewPath("spec.hostPlacement.affinity"), "affinity is required and must be set to either AnyAvailable or DedicatedHost"))
912+
} else {
913+
switch *placement.Affinity {
914+
case machinev1beta1.HostAffinityAnyAvailable:
915+
// Cannot have DedicatedHost set
916+
if placement.DedicatedHost != nil {
917+
errs = append(errs, field.Forbidden(field.NewPath("spec.hostPlacement.dedicatedHost"), "dedicatedHost is required when affinity is DedicatedHost, and forbidden otherwise"))
918+
}
919+
case machinev1beta1.HostAffinityDedicatedHost:
920+
// We need to make sure DedicatedHost is set with a HostID
921+
if placement.DedicatedHost == nil {
922+
errs = append(errs, field.Required(field.NewPath("spec.hostPlacement.dedicatedHost"), "dedicatedHost is required when affinity is DedicatedHost, and forbidden otherwise"))
923+
} else {
924+
// If not set, return required error. If it does not match pattern, return pattern failure message.
925+
if placement.DedicatedHost.ID == "" {
926+
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)"))
927+
} else if awsDedicatedHostNamePattern.FindStringSubmatch(placement.DedicatedHost.ID) == nil {
928+
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)"))
929+
}
930+
}
931+
default:
932+
errs = append(errs, field.Invalid(field.NewPath("spec.hostPlacement.affinity"), placement.Affinity, "affinity must be either AnyAvailable or DedicatedHost"))
933+
}
934+
}
935+
}
936+
900937
if len(errs) > 0 {
901938
return false, warnings, errs
902939
}

pkg/webhooks/machine_webhook_test.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,141 @@ func TestMachineCreation(t *testing.T) {
316316
},
317317
expectedError: "",
318318
},
319+
{
320+
name: "configure host placement with AnyAvailable affinity",
321+
platformType: osconfigv1.AWSPlatformType,
322+
clusterID: "aws-cluster",
323+
providerSpecValue: &kruntime.RawExtension{
324+
Object: &machinev1beta1.AWSMachineProviderConfig{
325+
AMI: machinev1beta1.AWSResourceReference{
326+
ID: ptr.To[string]("ami"),
327+
},
328+
InstanceType: "test",
329+
HostPlacement: &machinev1beta1.HostPlacement{
330+
Affinity: ptr.To(machinev1beta1.HostAffinityAnyAvailable),
331+
},
332+
},
333+
},
334+
expectedError: "",
335+
},
336+
{
337+
name: "configure host placement with invalid affinity",
338+
platformType: osconfigv1.AWSPlatformType,
339+
clusterID: "aws-cluster",
340+
providerSpecValue: &kruntime.RawExtension{
341+
Object: &machinev1beta1.AWSMachineProviderConfig{
342+
AMI: machinev1beta1.AWSResourceReference{
343+
ID: ptr.To[string]("ami"),
344+
},
345+
InstanceType: "test",
346+
HostPlacement: &machinev1beta1.HostPlacement{
347+
Affinity: ptr.To(machinev1beta1.HostAffinity("invalid")),
348+
},
349+
},
350+
},
351+
expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.affinity: Invalid value: \"invalid\": affinity must be either AnyAvailable or DedicatedHost",
352+
},
353+
{
354+
name: "configure host placement dedicatedHost without dedicatedHost",
355+
platformType: osconfigv1.AWSPlatformType,
356+
clusterID: "aws-cluster",
357+
providerSpecValue: &kruntime.RawExtension{
358+
Object: &machinev1beta1.AWSMachineProviderConfig{
359+
AMI: machinev1beta1.AWSResourceReference{
360+
ID: ptr.To[string]("ami"),
361+
},
362+
InstanceType: "test",
363+
HostPlacement: &machinev1beta1.HostPlacement{
364+
Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost),
365+
},
366+
},
367+
},
368+
expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: spec.hostPlacement.dedicatedHost: Required value: dedicatedHost is required when affinity is DedicatedHost",
369+
},
370+
{
371+
name: "configure host placement dedicatedHost with valid ID",
372+
platformType: osconfigv1.AWSPlatformType,
373+
clusterID: "aws-cluster",
374+
providerSpecValue: &kruntime.RawExtension{
375+
Object: &machinev1beta1.AWSMachineProviderConfig{
376+
AMI: machinev1beta1.AWSResourceReference{
377+
ID: ptr.To[string]("ami"),
378+
},
379+
InstanceType: "test",
380+
HostPlacement: &machinev1beta1.HostPlacement{
381+
Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost),
382+
DedicatedHost: &machinev1beta1.DedicatedHost{ID: "h-1234567890abcdef0"},
383+
},
384+
},
385+
},
386+
expectedError: "",
387+
},
388+
{
389+
name: "configure host placement AnyAvailable forbids dedicatedHost",
390+
platformType: osconfigv1.AWSPlatformType,
391+
clusterID: "aws-cluster",
392+
providerSpecValue: &kruntime.RawExtension{
393+
Object: &machinev1beta1.AWSMachineProviderConfig{
394+
AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")},
395+
InstanceType: "test",
396+
HostPlacement: &machinev1beta1.HostPlacement{
397+
Affinity: ptr.To(machinev1beta1.HostAffinityAnyAvailable),
398+
DedicatedHost: &machinev1beta1.DedicatedHost{ID: "h-09dcf61cb388b0149"},
399+
},
400+
},
401+
},
402+
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",
403+
},
404+
{
405+
name: "configure host placement dedicatedHost with empty ID",
406+
platformType: osconfigv1.AWSPlatformType,
407+
clusterID: "aws-cluster",
408+
providerSpecValue: &kruntime.RawExtension{
409+
Object: &machinev1beta1.AWSMachineProviderConfig{
410+
AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")},
411+
InstanceType: "test",
412+
HostPlacement: &machinev1beta1.HostPlacement{
413+
Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost),
414+
DedicatedHost: &machinev1beta1.DedicatedHost{ID: ""},
415+
},
416+
},
417+
},
418+
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)",
419+
},
420+
{
421+
name: "configure host placement dedicatedHost with ID not set",
422+
platformType: osconfigv1.AWSPlatformType,
423+
clusterID: "aws-cluster",
424+
providerSpecValue: &kruntime.RawExtension{
425+
Object: &machinev1beta1.AWSMachineProviderConfig{
426+
AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")},
427+
InstanceType: "test",
428+
HostPlacement: &machinev1beta1.HostPlacement{
429+
Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost),
430+
DedicatedHost: &machinev1beta1.DedicatedHost{},
431+
},
432+
},
433+
},
434+
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)",
435+
},
436+
{
437+
name: "configure host placement dedicatedHost with invalid ID",
438+
platformType: osconfigv1.AWSPlatformType,
439+
clusterID: "aws-cluster",
440+
providerSpecValue: &kruntime.RawExtension{
441+
Object: &machinev1beta1.AWSMachineProviderConfig{
442+
AMI: machinev1beta1.AWSResourceReference{ID: ptr.To[string]("ami")},
443+
InstanceType: "test",
444+
HostPlacement: &machinev1beta1.HostPlacement{
445+
Affinity: ptr.To(machinev1beta1.HostAffinityDedicatedHost),
446+
DedicatedHost: &machinev1beta1.DedicatedHost{
447+
ID: "invalid",
448+
},
449+
},
450+
},
451+
},
452+
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)",
453+
},
319454
{
320455
name: "with Azure and a nil provider spec value",
321456
platformType: osconfigv1.AzurePlatformType,

vendor/github.com/openshift/api/features/features.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)