From 5e0e4700e131776d87425589968dfd663ea16331 Mon Sep 17 00:00:00 2001 From: Mulham Raee Date: Mon, 16 Sep 2024 12:57:25 +0200 Subject: [PATCH] Add support to specifiy dedicated host ID and Affinity --- api/v1beta1/awscluster_conversion.go | 1 + api/v1beta1/awsmachine_conversion.go | 4 ++ api/v1beta1/zz_generated.conversion.go | 10 ++-- api/v1beta2/awsmachine_types.go | 36 +++++++++++- api/v1beta2/types.go | 12 +++- api/v1beta2/zz_generated.deepcopy.go | 35 +++++++++++ ...ster.x-k8s.io_awsmanagedcontrolplanes.yaml | 58 +++++++++++++++++-- ...tructure.cluster.x-k8s.io_awsclusters.yaml | 29 +++++++++- ...tructure.cluster.x-k8s.io_awsmachines.yaml | 25 +++++++- ....cluster.x-k8s.io_awsmachinetemplates.yaml | 25 +++++++- pkg/cloud/services/ec2/instances.go | 8 ++- 11 files changed, 226 insertions(+), 17 deletions(-) diff --git a/api/v1beta1/awscluster_conversion.go b/api/v1beta1/awscluster_conversion.go index 954fea7a4c..64c6944d60 100644 --- a/api/v1beta1/awscluster_conversion.go +++ b/api/v1beta1/awscluster_conversion.go @@ -60,6 +60,7 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error { dst.Status.Bastion.PrivateDNSName = restored.Status.Bastion.PrivateDNSName dst.Status.Bastion.PublicIPOnLaunch = restored.Status.Bastion.PublicIPOnLaunch dst.Status.Bastion.CapacityReservationID = restored.Status.Bastion.CapacityReservationID + dst.Status.Bastion.HostPlacement = restored.Status.Bastion.HostPlacement } dst.Spec.Partition = restored.Spec.Partition diff --git a/api/v1beta1/awsmachine_conversion.go b/api/v1beta1/awsmachine_conversion.go index 6044416cdf..95eb2b24b1 100644 --- a/api/v1beta1/awsmachine_conversion.go +++ b/api/v1beta1/awsmachine_conversion.go @@ -42,6 +42,8 @@ func (src *AWSMachine) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.PrivateDNSName = restored.Spec.PrivateDNSName dst.Spec.SecurityGroupOverrides = restored.Spec.SecurityGroupOverrides dst.Spec.CapacityReservationID = restored.Spec.CapacityReservationID + dst.Spec.HostPlacement = restored.Spec.HostPlacement + if restored.Spec.ElasticIPPool != nil { if dst.Spec.ElasticIPPool == nil { dst.Spec.ElasticIPPool = &infrav1.ElasticIPPool{} @@ -104,6 +106,8 @@ func (r *AWSMachineTemplate) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.PrivateDNSName = restored.Spec.Template.Spec.PrivateDNSName dst.Spec.Template.Spec.SecurityGroupOverrides = restored.Spec.Template.Spec.SecurityGroupOverrides dst.Spec.Template.Spec.CapacityReservationID = restored.Spec.Template.Spec.CapacityReservationID + dst.Spec.Template.Spec.HostPlacement = restored.Spec.Template.Spec.HostPlacement + if restored.Spec.Template.Spec.ElasticIPPool != nil { if dst.Spec.Template.Spec.ElasticIPPool == nil { dst.Spec.Template.Spec.ElasticIPPool = &infrav1.ElasticIPPool{} diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index a85eaf08d3..c0b6dc9dbd 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -1371,7 +1371,7 @@ func autoConvert_v1beta1_AWSMachineSpec_To_v1beta2_AWSMachineSpec(in *AWSMachine out.Ignition = nil } out.SpotMarketOptions = (*v1beta2.SpotMarketOptions)(unsafe.Pointer(in.SpotMarketOptions)) - out.Tenancy = in.Tenancy + out.Tenancy = v1beta2.Tenancy(in.Tenancy) return nil } @@ -1431,7 +1431,8 @@ func autoConvert_v1beta2_AWSMachineSpec_To_v1beta1_AWSMachineSpec(in *v1beta2.AW out.SpotMarketOptions = (*SpotMarketOptions)(unsafe.Pointer(in.SpotMarketOptions)) // WARNING: in.PlacementGroupName requires manual conversion: does not exist in peer-type // WARNING: in.PlacementGroupPartition requires manual conversion: does not exist in peer-type - out.Tenancy = in.Tenancy + out.Tenancy = string(in.Tenancy) + // WARNING: in.HostPlacement requires manual conversion: does not exist in peer-type // WARNING: in.PrivateDNSName requires manual conversion: does not exist in peer-type // WARNING: in.CapacityReservationID requires manual conversion: does not exist in peer-type return nil @@ -1997,7 +1998,7 @@ func autoConvert_v1beta1_Instance_To_v1beta2_Instance(in *Instance, out *v1beta2 out.Tags = *(*map[string]string)(unsafe.Pointer(&in.Tags)) out.AvailabilityZone = in.AvailabilityZone out.SpotMarketOptions = (*v1beta2.SpotMarketOptions)(unsafe.Pointer(in.SpotMarketOptions)) - out.Tenancy = in.Tenancy + out.Tenancy = v1beta2.Tenancy(in.Tenancy) out.VolumeIDs = *(*[]string)(unsafe.Pointer(&in.VolumeIDs)) return nil } @@ -2030,7 +2031,8 @@ func autoConvert_v1beta2_Instance_To_v1beta1_Instance(in *v1beta2.Instance, out out.SpotMarketOptions = (*SpotMarketOptions)(unsafe.Pointer(in.SpotMarketOptions)) // WARNING: in.PlacementGroupName requires manual conversion: does not exist in peer-type // WARNING: in.PlacementGroupPartition requires manual conversion: does not exist in peer-type - out.Tenancy = in.Tenancy + out.Tenancy = string(in.Tenancy) + // WARNING: in.HostPlacement requires manual conversion: does not exist in peer-type out.VolumeIDs = *(*[]string)(unsafe.Pointer(&in.VolumeIDs)) // WARNING: in.InstanceMetadataOptions requires manual conversion: does not exist in peer-type // WARNING: in.PrivateDNSName requires manual conversion: does not exist in peer-type diff --git a/api/v1beta2/awsmachine_types.go b/api/v1beta2/awsmachine_types.go index 39a649a0e5..2926e69e9f 100644 --- a/api/v1beta2/awsmachine_types.go +++ b/api/v1beta2/awsmachine_types.go @@ -54,6 +54,18 @@ const ( IgnitionStorageTypeOptionUnencryptedUserData = IgnitionStorageTypeOption("UnencryptedUserData") ) +// Tenancy defines the different tenancy options for EC2 instance. +type Tenancy string + +const ( + // TenancyDefault means that the EC2 instance will be launched into a shared host. + TenancyDefault = Tenancy("default") + // TenancyDedicated means that AWS will create and allocate a physical host for the EC2 instance (1 instance per host). + TenancyDedicated = Tenancy("dedicated") + // TenancyHost means that the EC2 instance will be launched into one of the user's pre-allocated physical hosts (multiple instances per host). + TenancyHost = Tenancy("host") +) + // AWSMachineSpec defines the desired state of an Amazon EC2 instance. type AWSMachineSpec struct { // ProviderID is the unique identifier as specified by the cloud provider. @@ -186,9 +198,18 @@ type AWSMachineSpec struct { PlacementGroupPartition int64 `json:"placementGroupPartition,omitempty"` // Tenancy indicates if instance should run on shared or single-tenant hardware. + // If host tenancy is used: + // - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + // - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + // see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement + // // +optional // +kubebuilder:validation:Enum:=default;dedicated;host - Tenancy string `json:"tenancy,omitempty"` + Tenancy Tenancy `json:"tenancy,omitempty"` + + // HostPlacement denotes the placement settings for the instance when tenancy=host. + // +optional + HostPlacement *HostPlacement `json:"hostPlacment,omitempty"` // PrivateDNSName is the options for the instance hostname. // +optional @@ -199,6 +220,19 @@ type AWSMachineSpec struct { CapacityReservationID *string `json:"capacityReservationId,omitempty"` } +// HostPlacement denotes the placement settings for the instance when tenancy=host. +type HostPlacement struct { + // HostID pins the instance to a sepcific Dedicated Host. + // +optional + HostID *string `json:"hostID,omitempty"` + + // Affinity indicates the affinity setting between the instance and a Dedicated Host. + // When affinity is set to host, an instance launched onto a specific host always restarts on the same host if stopped. This applies to both targeted and untargeted launches. + // +optional + // +kubebuilder:validation:Enum:=default;host + Affinity *string `json:"affinity,omitempty"` +} + // CloudInit defines options related to the bootstrapping systems where // CloudInit is used. type CloudInit struct { diff --git a/api/v1beta2/types.go b/api/v1beta2/types.go index 978d5310f2..6c34e1fe71 100644 --- a/api/v1beta2/types.go +++ b/api/v1beta2/types.go @@ -239,8 +239,18 @@ type Instance struct { PlacementGroupPartition int64 `json:"placementGroupPartition,omitempty"` // Tenancy indicates if instance should run on shared or single-tenant hardware. + // If host tenancy is used: + // - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + // - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + // see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement + // + // +optional + // +kubebuilder:validation:Enum:=default;dedicated;host + Tenancy Tenancy `json:"tenancy,omitempty"` + + // HostPlacement denotes the placement settings for the instance when tenancy=host. // +optional - Tenancy string `json:"tenancy,omitempty"` + HostPlacement *HostPlacement `json:"hostPlacment,omitempty"` // IDs of the instance's volumes // +optional diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index b3eaa6c08d..5f3948df35 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -762,6 +762,11 @@ func (in *AWSMachineSpec) DeepCopyInto(out *AWSMachineSpec) { *out = new(SpotMarketOptions) (*in).DeepCopyInto(*out) } + if in.HostPlacement != nil { + in, out := &in.HostPlacement, &out.HostPlacement + *out = new(HostPlacement) + (*in).DeepCopyInto(*out) + } if in.PrivateDNSName != nil { in, out := &in.PrivateDNSName, &out.PrivateDNSName *out = new(PrivateDNSName) @@ -1336,6 +1341,31 @@ func (in *Filter) DeepCopy() *Filter { 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.HostID != nil { + in, out := &in.HostID, &out.HostID + *out = new(string) + **out = **in + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(string) + **out = **in + } +} + +// 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 *IPAMPool) DeepCopyInto(out *IPAMPool) { *out = *in @@ -1579,6 +1609,11 @@ func (in *Instance) DeepCopyInto(out *Instance) { *out = new(SpotMarketOptions) (*in).DeepCopyInto(*out) } + if in.HostPlacement != nil { + in, out := &in.HostPlacement, &out.HostPlacement + *out = new(HostPlacement) + (*in).DeepCopyInto(*out) + } if in.VolumeIDs != nil { in, out := &in.VolumeIDs, &out.VolumeIDs *out = make([]string, len(*in)) diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml index 345b3f4379..70e2405e10 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml @@ -1133,6 +1133,23 @@ spec: description: Specifies whether enhanced networking with ENA is enabled. type: boolean + hostPlacment: + description: HostPlacement denotes the placement settings for + the instance when tenancy=host. + properties: + affinity: + description: |- + Affinity indicates the affinity setting between the instance and a Dedicated Host. + When affinity is set to host, an instance launched onto a specific host always restarts on the same host if stopped. This applies to both targeted and untargeted launches. + enum: + - default + - host + type: string + hostID: + description: HostID pins the instance to a sepcific Dedicated + Host. + type: string + type: object iamProfile: description: The name of the IAM instance profile associated with the instance, if applicable. @@ -1374,8 +1391,16 @@ spec: description: The tags associated with the instance. type: object tenancy: - description: Tenancy indicates if instance should run on shared - or single-tenant hardware. + description: |- + Tenancy indicates if instance should run on shared or single-tenant hardware. + If host tenancy is used: + - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement + enum: + - default + - dedicated + - host type: string type: description: The instance type. @@ -3177,6 +3202,23 @@ spec: description: Specifies whether enhanced networking with ENA is enabled. type: boolean + hostPlacment: + description: HostPlacement denotes the placement settings for + the instance when tenancy=host. + properties: + affinity: + description: |- + Affinity indicates the affinity setting between the instance and a Dedicated Host. + When affinity is set to host, an instance launched onto a specific host always restarts on the same host if stopped. This applies to both targeted and untargeted launches. + enum: + - default + - host + type: string + hostID: + description: HostID pins the instance to a sepcific Dedicated + Host. + type: string + type: object iamProfile: description: The name of the IAM instance profile associated with the instance, if applicable. @@ -3418,8 +3460,16 @@ spec: description: The tags associated with the instance. type: object tenancy: - description: Tenancy indicates if instance should run on shared - or single-tenant hardware. + description: |- + Tenancy indicates if instance should run on shared or single-tenant hardware. + If host tenancy is used: + - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement + enum: + - default + - dedicated + - host type: string type: description: The instance type. diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml index f3eb3a2fc7..b4eb13aecf 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml @@ -2118,6 +2118,23 @@ spec: description: Specifies whether enhanced networking with ENA is enabled. type: boolean + hostPlacment: + description: HostPlacement denotes the placement settings for + the instance when tenancy=host. + properties: + affinity: + description: |- + Affinity indicates the affinity setting between the instance and a Dedicated Host. + When affinity is set to host, an instance launched onto a specific host always restarts on the same host if stopped. This applies to both targeted and untargeted launches. + enum: + - default + - host + type: string + hostID: + description: HostID pins the instance to a sepcific Dedicated + Host. + type: string + type: object iamProfile: description: The name of the IAM instance profile associated with the instance, if applicable. @@ -2359,8 +2376,16 @@ spec: description: The tags associated with the instance. type: object tenancy: - description: Tenancy indicates if instance should run on shared - or single-tenant hardware. + description: |- + Tenancy indicates if instance should run on shared or single-tenant hardware. + If host tenancy is used: + - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement + enum: + - default + - dedicated + - host type: string type: description: The instance type. diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml index c02466fa59..82d1b5c6f7 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml @@ -692,6 +692,23 @@ spec: - message: allowed values are 'none' and 'amazon-pool' rule: self in ['none','amazon-pool'] type: object + hostPlacment: + description: HostPlacement denotes the placement settings for the + instance when tenancy=host. + properties: + affinity: + description: |- + Affinity indicates the affinity setting between the instance and a Dedicated Host. + When affinity is set to host, an instance launched onto a specific host always restarts on the same host if stopped. This applies to both targeted and untargeted launches. + enum: + - default + - host + type: string + hostID: + description: HostID pins the instance to a sepcific Dedicated + Host. + type: string + type: object iamInstanceProfile: description: IAMInstanceProfile is a name of an IAM instance profile to assign to the instance @@ -1085,8 +1102,12 @@ spec: type: string type: object tenancy: - description: Tenancy indicates if instance should run on shared or - single-tenant hardware. + description: |- + Tenancy indicates if instance should run on shared or single-tenant hardware. + If host tenancy is used: + - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement enum: - default - dedicated diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml index 501a837555..0f998143b7 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml @@ -622,6 +622,23 @@ spec: - message: allowed values are 'none' and 'amazon-pool' rule: self in ['none','amazon-pool'] type: object + hostPlacment: + description: HostPlacement denotes the placement settings + for the instance when tenancy=host. + properties: + affinity: + description: |- + Affinity indicates the affinity setting between the instance and a Dedicated Host. + When affinity is set to host, an instance launched onto a specific host always restarts on the same host if stopped. This applies to both targeted and untargeted launches. + enum: + - default + - host + type: string + hostID: + description: HostID pins the instance to a sepcific Dedicated + Host. + type: string + type: object iamInstanceProfile: description: IAMInstanceProfile is a name of an IAM instance profile to assign to the instance @@ -1022,8 +1039,12 @@ spec: type: string type: object tenancy: - description: Tenancy indicates if instance should run on shared - or single-tenant hardware. + description: |- + Tenancy indicates if instance should run on shared or single-tenant hardware. + If host tenancy is used: + - if there are no Dedicated Hosts with auto-placement enabled that match the instance type configuration, then it is required to set '.spec.hostPlacment.hostID' or no host would be picked. + - if '.spec.hostPlacment.hostID' is set, the instance will be launched into the specified Host, disregarding any auto-placement settings. + see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-understanding.html#dedicated-hosts-auto-placement enum: - default - dedicated diff --git a/pkg/cloud/services/ec2/instances.go b/pkg/cloud/services/ec2/instances.go index ac11fea9fd..8054748cef 100644 --- a/pkg/cloud/services/ec2/instances.go +++ b/pkg/cloud/services/ec2/instances.go @@ -244,6 +244,7 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte, use input.InstanceMetadataOptions = scope.AWSMachine.Spec.InstanceMetadataOptions input.Tenancy = scope.AWSMachine.Spec.Tenancy + input.HostPlacement = scope.AWSMachine.Spec.HostPlacement input.PlacementGroupName = scope.AWSMachine.Spec.PlacementGroupName @@ -653,7 +654,12 @@ func (s *Service) runInstance(role string, i *infrav1.Instance) (*infrav1.Instan if i.Tenancy != "" { input.Placement = &ec2.Placement{ - Tenancy: &i.Tenancy, + Tenancy: ptr.To(string(i.Tenancy)), + } + + if i.Tenancy == infrav1.TenancyHost && i.HostPlacement != nil { + input.Placement.Affinity = i.HostPlacement.Affinity + input.Placement.HostId = i.HostPlacement.HostID } }