Skip to content

Commit a474b6f

Browse files
AmitSahastraKun483
authored andcommitted
PCP-5519: Cherry pick HostResourceGroup upstream PR in palette fork (#990)
1 parent 54dfb1a commit a474b6f

24 files changed

+1499
-104
lines changed

api/v1beta1/awscluster_conversion.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
6060
dst.Status.Bastion.PrivateDNSName = restored.Status.Bastion.PrivateDNSName
6161
dst.Status.Bastion.PublicIPOnLaunch = restored.Status.Bastion.PublicIPOnLaunch
6262
dst.Status.Bastion.CapacityReservationID = restored.Status.Bastion.CapacityReservationID
63+
dst.Status.Bastion.MarketType = restored.Status.Bastion.MarketType
64+
dst.Status.Bastion.HostAffinity = restored.Status.Bastion.HostAffinity
65+
dst.Status.Bastion.HostID = restored.Status.Bastion.HostID
66+
dst.Status.Bastion.HostResourceGroupArn = restored.Status.Bastion.HostResourceGroupArn
67+
dst.Status.Bastion.CapacityReservationPreference = restored.Status.Bastion.CapacityReservationPreference
68+
dst.Status.Bastion.CPUOptions = restored.Status.Bastion.CPUOptions
69+
if restored.Status.Bastion.DynamicHostAllocation != nil {
70+
dst.Status.Bastion.DynamicHostAllocation = restored.Status.Bastion.DynamicHostAllocation
71+
}
6372
}
6473
dst.Spec.Partition = restored.Spec.Partition
6574

api/v1beta1/awsmachine_conversion.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ func (src *AWSMachine) ConvertTo(dstRaw conversion.Hub) error {
4242
dst.Spec.PrivateDNSName = restored.Spec.PrivateDNSName
4343
dst.Spec.SecurityGroupOverrides = restored.Spec.SecurityGroupOverrides
4444
dst.Spec.CapacityReservationID = restored.Spec.CapacityReservationID
45+
dst.Spec.MarketType = restored.Spec.MarketType
46+
dst.Spec.HostID = restored.Spec.HostID
47+
dst.Spec.HostResourceGroupArn = restored.Spec.HostResourceGroupArn
48+
dst.Spec.LicenseConfigurationArns = restored.Spec.LicenseConfigurationArns
49+
dst.Spec.HostAffinity = restored.Spec.HostAffinity
50+
dst.Spec.CapacityReservationPreference = restored.Spec.CapacityReservationPreference
51+
dst.Spec.NetworkInterfaceType = restored.Spec.NetworkInterfaceType
4552
if restored.Spec.ElasticIPPool != nil {
4653
if dst.Spec.ElasticIPPool == nil {
4754
dst.Spec.ElasticIPPool = &infrav1.ElasticIPPool{}
@@ -104,6 +111,13 @@ func (r *AWSMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
104111
dst.Spec.Template.Spec.PrivateDNSName = restored.Spec.Template.Spec.PrivateDNSName
105112
dst.Spec.Template.Spec.SecurityGroupOverrides = restored.Spec.Template.Spec.SecurityGroupOverrides
106113
dst.Spec.Template.Spec.CapacityReservationID = restored.Spec.Template.Spec.CapacityReservationID
114+
dst.Spec.Template.Spec.MarketType = restored.Spec.Template.Spec.MarketType
115+
dst.Spec.Template.Spec.HostID = restored.Spec.Template.Spec.HostID
116+
dst.Spec.Template.Spec.HostResourceGroupArn = restored.Spec.Template.Spec.HostResourceGroupArn
117+
dst.Spec.Template.Spec.LicenseConfigurationArns = restored.Spec.Template.Spec.LicenseConfigurationArns
118+
dst.Spec.Template.Spec.HostAffinity = restored.Spec.Template.Spec.HostAffinity
119+
dst.Spec.Template.Spec.CapacityReservationPreference = restored.Spec.Template.Spec.CapacityReservationPreference
120+
dst.Spec.Template.Spec.NetworkInterfaceType = restored.Spec.Template.Spec.NetworkInterfaceType
107121
if restored.Spec.Template.Spec.ElasticIPPool != nil {
108122
if dst.Spec.Template.Spec.ElasticIPPool == nil {
109123
dst.Spec.Template.Spec.ElasticIPPool = &infrav1.ElasticIPPool{}

api/v1beta1/zz_generated.conversion.go

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

api/v1beta2/awsmachine_types.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,68 @@ type AWSMachineSpec struct {
197197
// CapacityReservationID specifies the target Capacity Reservation into which the instance should be launched.
198198
// +optional
199199
CapacityReservationID *string `json:"capacityReservationId,omitempty"`
200+
201+
// MarketType specifies the type of market for the EC2 instance. Valid values include:
202+
// "OnDemand" (default): The instance runs as a standard OnDemand instance.
203+
// "Spot": The instance runs as a Spot instance. When SpotMarketOptions is provided, the marketType defaults to "Spot".
204+
// "CapacityBlock": The instance utilizes pre-purchased compute capacity (capacity blocks) with AWS Capacity Reservations.
205+
// If this value is selected, CapacityReservationID must be specified to identify the target reservation.
206+
// If marketType is not specified and spotMarketOptions is provided, the marketType defaults to "Spot".
207+
// +optional
208+
MarketType MarketType `json:"marketType,omitempty"`
209+
210+
// HostID specifies the Dedicated Host on which the instance must be started.
211+
// This field is mutually exclusive with DynamicHostAllocation.
212+
// +kubebuilder:validation:Pattern=`^h-[0-9a-f]{17}$`
213+
// +kubebuilder:validation:MaxLength=19
214+
// +optional
215+
HostID *string `json:"hostID,omitempty"`
216+
217+
// HostResourceGroupArn specifies the Dedicated Host Resource Group ARN on which the instance must be started.
218+
// This field is mutually exclusive with DynamicHostAllocation and HostID.
219+
// Note: The instance's AMI licenses must match the licenses associated with the host resource group.
220+
// If the host resource group has no associated licenses, ensure the AMI also has no special licensing requirements.
221+
// +kubebuilder:validation:Pattern=`^arn:aws[a-z\-]*:resource-groups:[a-z0-9\-]+:[0-9]{12}:group/[a-zA-Z0-9\-_]+$`
222+
// +optional
223+
HostResourceGroupArn *string `json:"hostResourceGroupArn,omitempty"`
224+
225+
// LicenseConfigurationArns specifies the License Configuration ARNs to associate with the instance.
226+
// This field is required when HostResourceGroupArn is specified to ensure proper license compliance.
227+
// +kubebuilder:validation:MaxItems=10
228+
// +optional
229+
LicenseConfigurationArns []string `json:"licenseConfigurationArns,omitempty"`
230+
231+
// HostAffinity specifies the dedicated host affinity setting for the instance.
232+
// When HostAffinity is set to host, an instance started onto a specific host always restarts on the same host if stopped.
233+
// When HostAffinity is set to default, and you stop and restart the instance, it can be restarted on any available host.
234+
// When HostAffinity is defined, HostID is required.
235+
// +optional
236+
// +kubebuilder:validation:Enum:=default;host
237+
// +kubebuilder:default=host
238+
HostAffinity *string `json:"hostAffinity,omitempty"`
239+
240+
// DynamicHostAllocation enables automatic allocation of a single dedicated host.
241+
// This field is mutually exclusive with HostID and always allocates exactly one host.
242+
// Cost effectiveness of allocating a single instance on a dedicated host may vary
243+
// depending on the instance type and the region.
244+
// +optional
245+
DynamicHostAllocation *DynamicHostAllocationSpec `json:"dynamicHostAllocation,omitempty"`
246+
247+
// CapacityReservationPreference specifies the preference for use of Capacity Reservations by the instance. Valid values include:
248+
// "Open": The instance may make use of open Capacity Reservations that match its AZ and InstanceType
249+
// "None": The instance may not make use of any Capacity Reservations. This is to conserve open reservations for desired workloads
250+
// "CapacityReservationsOnly": The instance will only run if matched or targeted to a Capacity Reservation. Note that this is incompatible with a MarketType of `Spot`
251+
// +kubebuilder:validation:Enum="";None;CapacityReservationsOnly;Open
252+
// +optional
253+
CapacityReservationPreference CapacityReservationPreference `json:"capacityReservationPreference,omitempty"`
254+
}
255+
256+
// DynamicHostAllocationSpec defines the configuration for dynamic dedicated host allocation.
257+
// This specification always allocates exactly one dedicated host per machine.
258+
type DynamicHostAllocationSpec struct {
259+
// Tags to apply to the allocated dedicated host.
260+
// +optional
261+
Tags map[string]string `json:"tags,omitempty"`
200262
}
201263

202264
// CloudInit defines options related to the bootstrapping systems where

api/v1beta2/awsmachine_webhook.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,39 @@ func (r *AWSMachine) validateAdditionalSecurityGroups() field.ErrorList {
418418
return allErrs
419419
}
420420

421+
func (r *AWSMachine) validateHostAllocation() field.ErrorList {
422+
var allErrs field.ErrorList
423+
424+
// Check if multiple host allocation options are specified
425+
hasHostID := r.Spec.HostID != nil && len(*r.Spec.HostID) > 0
426+
hasHostResourceGroupArn := r.Spec.HostResourceGroupArn != nil && len(*r.Spec.HostResourceGroupArn) > 0
427+
hasDynamicHostAllocation := r.Spec.DynamicHostAllocation != nil
428+
429+
count := 0
430+
if hasHostID {
431+
count++
432+
}
433+
if hasHostResourceGroupArn {
434+
count++
435+
}
436+
if hasDynamicHostAllocation {
437+
count++
438+
}
439+
440+
if count > 1 {
441+
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "hostID, hostResourceGroupArn, and dynamicHostAllocation are mutually exclusive"))
442+
}
443+
444+
// Validate licenseConfigurationArns is required when hostResourceGroupArn is specified
445+
if hasHostResourceGroupArn {
446+
if len(r.Spec.LicenseConfigurationArns) == 0 {
447+
allErrs = append(allErrs, field.Required(field.NewPath("spec", "licenseConfigurationArns"), "licenseConfigurationArns is required when hostResourceGroupArn is specified"))
448+
}
449+
}
450+
451+
return allErrs
452+
}
453+
421454
func (r *AWSMachine) validateSSHKeyName() field.ErrorList {
422455
return validateSSHKeyName(r.Spec.SSHKeyName)
423456
}

api/v1beta2/awsmachine_webhook_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,118 @@ func TestAWSMachineCreate(t *testing.T) {
436436
},
437437
wantErr: true,
438438
},
439+
{
440+
name: "hostID and dynamicHostAllocation are mutually exclusive",
441+
machine: &AWSMachine{
442+
Spec: AWSMachineSpec{
443+
InstanceType: "test",
444+
HostID: aws.String("h-1234567890abcdef0"),
445+
DynamicHostAllocation: &DynamicHostAllocationSpec{
446+
Tags: map[string]string{
447+
"Environment": "test",
448+
},
449+
},
450+
},
451+
},
452+
wantErr: true,
453+
},
454+
{
455+
name: "hostID alone is valid",
456+
machine: &AWSMachine{
457+
Spec: AWSMachineSpec{
458+
InstanceType: "test",
459+
HostID: aws.String("h-1234567890abcdef0"),
460+
},
461+
},
462+
wantErr: false,
463+
},
464+
{
465+
name: "dynamicHostAllocation alone is valid",
466+
machine: &AWSMachine{
467+
Spec: AWSMachineSpec{
468+
InstanceType: "test",
469+
DynamicHostAllocation: &DynamicHostAllocationSpec{
470+
Tags: map[string]string{
471+
"Environment": "test",
472+
},
473+
},
474+
},
475+
},
476+
wantErr: false,
477+
},
478+
{
479+
name: "hostResourceGroupArn alone is valid",
480+
machine: &AWSMachine{
481+
Spec: AWSMachineSpec{
482+
InstanceType: "test",
483+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
484+
},
485+
},
486+
wantErr: false,
487+
},
488+
{
489+
name: "hostID and hostResourceGroupArn are mutually exclusive",
490+
machine: &AWSMachine{
491+
Spec: AWSMachineSpec{
492+
InstanceType: "test",
493+
HostID: aws.String("h-1234567890abcdef0"),
494+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
495+
},
496+
},
497+
wantErr: true,
498+
},
499+
{
500+
name: "hostResourceGroupArn and dynamicHostAllocation are mutually exclusive",
501+
machine: &AWSMachine{
502+
Spec: AWSMachineSpec{
503+
InstanceType: "test",
504+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
505+
DynamicHostAllocation: &DynamicHostAllocationSpec{
506+
Tags: map[string]string{
507+
"Environment": "test",
508+
},
509+
},
510+
},
511+
},
512+
wantErr: true,
513+
},
514+
{
515+
name: "all three host allocation options are mutually exclusive",
516+
machine: &AWSMachine{
517+
Spec: AWSMachineSpec{
518+
InstanceType: "test",
519+
HostID: aws.String("h-1234567890abcdef0"),
520+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
521+
DynamicHostAllocation: &DynamicHostAllocationSpec{
522+
Tags: map[string]string{
523+
"Environment": "test",
524+
},
525+
},
526+
},
527+
},
528+
wantErr: true,
529+
},
530+
{
531+
name: "hostResourceGroupArn without licenseConfigurationArns should fail",
532+
machine: &AWSMachine{
533+
Spec: AWSMachineSpec{
534+
InstanceType: "test",
535+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
536+
},
537+
},
538+
wantErr: true,
539+
},
540+
{
541+
name: "hostResourceGroupArn with licenseConfigurationArns should succeed",
542+
machine: &AWSMachine{
543+
Spec: AWSMachineSpec{
544+
InstanceType: "test",
545+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
546+
LicenseConfigurationArns: []string{"arn:aws:license-manager:us-west-2:259732043995:license-configuration:lic-4acd3f7c117b9e314cce36e46084d071"},
547+
},
548+
},
549+
wantErr: false,
550+
},
439551
}
440552
for _, tt := range tests {
441553
t.Run(tt.name, func(t *testing.T) {

api/v1beta2/awsmachinetemplate_webhook.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,42 @@ func (r *AWSMachineTemplate) validateIgnitionAndCloudInit() field.ErrorList {
171171

172172
return allErrs
173173
}
174+
175+
func (r *AWSMachineTemplate) validateHostAllocation() field.ErrorList {
176+
var allErrs field.ErrorList
177+
178+
spec := r.Spec.Template.Spec
179+
180+
// Check if multiple host allocation options are specified
181+
hasHostID := spec.HostID != nil && len(*spec.HostID) > 0
182+
hasHostResourceGroupArn := spec.HostResourceGroupArn != nil && len(*spec.HostResourceGroupArn) > 0
183+
hasDynamicHostAllocation := spec.DynamicHostAllocation != nil
184+
185+
count := 0
186+
if hasHostID {
187+
count++
188+
}
189+
if hasHostResourceGroupArn {
190+
count++
191+
}
192+
if hasDynamicHostAllocation {
193+
count++
194+
}
195+
196+
if count > 1 {
197+
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.template.spec"), "hostID, hostResourceGroupArn, and dynamicHostAllocation are mutually exclusive"))
198+
}
199+
200+
// Validate licenseConfigurationArns is required when hostResourceGroupArn is specified
201+
if hasHostResourceGroupArn {
202+
if len(spec.LicenseConfigurationArns) == 0 {
203+
allErrs = append(allErrs, field.Required(field.NewPath("spec", "template", "spec", "licenseConfigurationArns"), "licenseConfigurationArns is required when hostResourceGroupArn is specified"))
204+
}
205+
}
206+
207+
return allErrs
208+
}
209+
174210
func (r *AWSMachineTemplate) validateSSHKeyName() field.ErrorList {
175211
return validateSSHKeyName(r.Spec.Template.Spec.SSHKeyName)
176212
}

0 commit comments

Comments
 (0)