Skip to content

Commit 94f496d

Browse files
authored
PCP-5519: Cherry pick HostResourceGroup upstream PR in palette fork (#990) (#993)
1 parent d367148 commit 94f496d

25 files changed

+1999
-176
lines changed

api/v1beta1/awscluster_conversion.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
6262
dst.Status.Bastion.NetworkInterfaceType = restored.Status.Bastion.NetworkInterfaceType
6363
dst.Status.Bastion.CapacityReservationID = restored.Status.Bastion.CapacityReservationID
6464
dst.Status.Bastion.MarketType = restored.Status.Bastion.MarketType
65+
dst.Status.Bastion.HostAffinity = restored.Status.Bastion.HostAffinity
66+
dst.Status.Bastion.HostID = restored.Status.Bastion.HostID
67+
dst.Status.Bastion.HostResourceGroupArn = restored.Status.Bastion.HostResourceGroupArn
68+
dst.Status.Bastion.CapacityReservationPreference = restored.Status.Bastion.CapacityReservationPreference
69+
dst.Status.Bastion.CPUOptions = restored.Status.Bastion.CPUOptions
70+
if restored.Status.Bastion.DynamicHostAllocation != nil {
71+
dst.Status.Bastion.DynamicHostAllocation = restored.Status.Bastion.DynamicHostAllocation
72+
}
6573
}
6674
dst.Spec.Partition = restored.Spec.Partition
6775

api/v1beta1/awsmachine_conversion.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ func (src *AWSMachine) ConvertTo(dstRaw conversion.Hub) error {
4343
dst.Spec.SecurityGroupOverrides = restored.Spec.SecurityGroupOverrides
4444
dst.Spec.CapacityReservationID = restored.Spec.CapacityReservationID
4545
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
4651
dst.Spec.NetworkInterfaceType = restored.Spec.NetworkInterfaceType
4752
if restored.Spec.ElasticIPPool != nil {
4853
if dst.Spec.ElasticIPPool == nil {
@@ -107,6 +112,11 @@ func (r *AWSMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
107112
dst.Spec.Template.Spec.SecurityGroupOverrides = restored.Spec.Template.Spec.SecurityGroupOverrides
108113
dst.Spec.Template.Spec.CapacityReservationID = restored.Spec.Template.Spec.CapacityReservationID
109114
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
110120
dst.Spec.Template.Spec.NetworkInterfaceType = restored.Spec.Template.Spec.NetworkInterfaceType
111121
if restored.Spec.Template.Spec.ElasticIPPool != nil {
112122
if dst.Spec.Template.Spec.ElasticIPPool == nil {

api/v1beta1/zz_generated.conversion.go

Lines changed: 14 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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,59 @@ type AWSMachineSpec struct {
221221
// If marketType is not specified and spotMarketOptions is provided, the marketType defaults to "Spot".
222222
// +optional
223223
MarketType MarketType `json:"marketType,omitempty"`
224+
225+
// HostID specifies the Dedicated Host on which the instance must be started.
226+
// This field is mutually exclusive with DynamicHostAllocation.
227+
// +kubebuilder:validation:Pattern=`^h-[0-9a-f]{17}$`
228+
// +kubebuilder:validation:MaxLength=19
229+
// +optional
230+
HostID *string `json:"hostID,omitempty"`
231+
232+
// HostResourceGroupArn specifies the Dedicated Host Resource Group ARN on which the instance must be started.
233+
// This field is mutually exclusive with DynamicHostAllocation and HostID.
234+
// Note: The instance's AMI licenses must match the licenses associated with the host resource group.
235+
// If the host resource group has no associated licenses, ensure the AMI also has no special licensing requirements.
236+
// +kubebuilder:validation:Pattern=`^arn:aws[a-z\-]*:resource-groups:[a-z0-9\-]+:[0-9]{12}:group/[a-zA-Z0-9\-_]+$`
237+
// +optional
238+
HostResourceGroupArn *string `json:"hostResourceGroupArn,omitempty"`
239+
240+
// LicenseConfigurationArns specifies the License Configuration ARNs to associate with the instance.
241+
// This field is required when HostResourceGroupArn is specified to ensure proper license compliance.
242+
// +kubebuilder:validation:MaxItems=10
243+
// +optional
244+
LicenseConfigurationArns []string `json:"licenseConfigurationArns,omitempty"`
245+
246+
// HostAffinity specifies the dedicated host affinity setting for the instance.
247+
// When HostAffinity is set to host, an instance started onto a specific host always restarts on the same host if stopped.
248+
// When HostAffinity is set to default, and you stop and restart the instance, it can be restarted on any available host.
249+
// When HostAffinity is defined, HostID is required.
250+
// +optional
251+
// +kubebuilder:validation:Enum:=default;host
252+
// +kubebuilder:default=host
253+
HostAffinity *string `json:"hostAffinity,omitempty"`
254+
255+
// DynamicHostAllocation enables automatic allocation of a single dedicated host.
256+
// This field is mutually exclusive with HostID and always allocates exactly one host.
257+
// Cost effectiveness of allocating a single instance on a dedicated host may vary
258+
// depending on the instance type and the region.
259+
// +optional
260+
DynamicHostAllocation *DynamicHostAllocationSpec `json:"dynamicHostAllocation,omitempty"`
261+
262+
// CapacityReservationPreference specifies the preference for use of Capacity Reservations by the instance. Valid values include:
263+
// "Open": The instance may make use of open Capacity Reservations that match its AZ and InstanceType
264+
// "None": The instance may not make use of any Capacity Reservations. This is to conserve open reservations for desired workloads
265+
// "CapacityReservationsOnly": The instance will only run if matched or targeted to a Capacity Reservation. Note that this is incompatible with a MarketType of `Spot`
266+
// +kubebuilder:validation:Enum="";None;CapacityReservationsOnly;Open
267+
// +optional
268+
CapacityReservationPreference CapacityReservationPreference `json:"capacityReservationPreference,omitempty"`
269+
}
270+
271+
// DynamicHostAllocationSpec defines the configuration for dynamic dedicated host allocation.
272+
// This specification always allocates exactly one dedicated host per machine.
273+
type DynamicHostAllocationSpec struct {
274+
// Tags to apply to the allocated dedicated host.
275+
// +optional
276+
Tags map[string]string `json:"tags,omitempty"`
224277
}
225278

226279
// 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
@@ -458,6 +458,39 @@ func (r *AWSMachine) validateAdditionalSecurityGroups() field.ErrorList {
458458
return allErrs
459459
}
460460

461+
func (r *AWSMachine) validateHostAllocation() field.ErrorList {
462+
var allErrs field.ErrorList
463+
464+
// Check if multiple host allocation options are specified
465+
hasHostID := r.Spec.HostID != nil && len(*r.Spec.HostID) > 0
466+
hasHostResourceGroupArn := r.Spec.HostResourceGroupArn != nil && len(*r.Spec.HostResourceGroupArn) > 0
467+
hasDynamicHostAllocation := r.Spec.DynamicHostAllocation != nil
468+
469+
count := 0
470+
if hasHostID {
471+
count++
472+
}
473+
if hasHostResourceGroupArn {
474+
count++
475+
}
476+
if hasDynamicHostAllocation {
477+
count++
478+
}
479+
480+
if count > 1 {
481+
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "hostID, hostResourceGroupArn, and dynamicHostAllocation are mutually exclusive"))
482+
}
483+
484+
// Validate licenseConfigurationArns is required when hostResourceGroupArn is specified
485+
if hasHostResourceGroupArn {
486+
if len(r.Spec.LicenseConfigurationArns) == 0 {
487+
allErrs = append(allErrs, field.Required(field.NewPath("spec", "licenseConfigurationArns"), "licenseConfigurationArns is required when hostResourceGroupArn is specified"))
488+
}
489+
}
490+
491+
return allErrs
492+
}
493+
461494
func (r *AWSMachine) validateSSHKeyName() field.ErrorList {
462495
return validateSSHKeyName(r.Spec.SSHKeyName)
463496
}

api/v1beta2/awsmachine_webhook_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,118 @@ func TestAWSMachineCreate(t *testing.T) {
493493
},
494494
wantErr: true,
495495
},
496+
{
497+
name: "hostID and dynamicHostAllocation are mutually exclusive",
498+
machine: &AWSMachine{
499+
Spec: AWSMachineSpec{
500+
InstanceType: "test",
501+
HostID: aws.String("h-1234567890abcdef0"),
502+
DynamicHostAllocation: &DynamicHostAllocationSpec{
503+
Tags: map[string]string{
504+
"Environment": "test",
505+
},
506+
},
507+
},
508+
},
509+
wantErr: true,
510+
},
511+
{
512+
name: "hostID alone is valid",
513+
machine: &AWSMachine{
514+
Spec: AWSMachineSpec{
515+
InstanceType: "test",
516+
HostID: aws.String("h-1234567890abcdef0"),
517+
},
518+
},
519+
wantErr: false,
520+
},
521+
{
522+
name: "dynamicHostAllocation alone is valid",
523+
machine: &AWSMachine{
524+
Spec: AWSMachineSpec{
525+
InstanceType: "test",
526+
DynamicHostAllocation: &DynamicHostAllocationSpec{
527+
Tags: map[string]string{
528+
"Environment": "test",
529+
},
530+
},
531+
},
532+
},
533+
wantErr: false,
534+
},
535+
{
536+
name: "hostResourceGroupArn alone is valid",
537+
machine: &AWSMachine{
538+
Spec: AWSMachineSpec{
539+
InstanceType: "test",
540+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
541+
},
542+
},
543+
wantErr: false,
544+
},
545+
{
546+
name: "hostID and hostResourceGroupArn are mutually exclusive",
547+
machine: &AWSMachine{
548+
Spec: AWSMachineSpec{
549+
InstanceType: "test",
550+
HostID: aws.String("h-1234567890abcdef0"),
551+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
552+
},
553+
},
554+
wantErr: true,
555+
},
556+
{
557+
name: "hostResourceGroupArn and dynamicHostAllocation are mutually exclusive",
558+
machine: &AWSMachine{
559+
Spec: AWSMachineSpec{
560+
InstanceType: "test",
561+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
562+
DynamicHostAllocation: &DynamicHostAllocationSpec{
563+
Tags: map[string]string{
564+
"Environment": "test",
565+
},
566+
},
567+
},
568+
},
569+
wantErr: true,
570+
},
571+
{
572+
name: "all three host allocation options are mutually exclusive",
573+
machine: &AWSMachine{
574+
Spec: AWSMachineSpec{
575+
InstanceType: "test",
576+
HostID: aws.String("h-1234567890abcdef0"),
577+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
578+
DynamicHostAllocation: &DynamicHostAllocationSpec{
579+
Tags: map[string]string{
580+
"Environment": "test",
581+
},
582+
},
583+
},
584+
},
585+
wantErr: true,
586+
},
587+
{
588+
name: "hostResourceGroupArn without licenseConfigurationArns should fail",
589+
machine: &AWSMachine{
590+
Spec: AWSMachineSpec{
591+
InstanceType: "test",
592+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
593+
},
594+
},
595+
wantErr: true,
596+
},
597+
{
598+
name: "hostResourceGroupArn with licenseConfigurationArns should succeed",
599+
machine: &AWSMachine{
600+
Spec: AWSMachineSpec{
601+
InstanceType: "test",
602+
HostResourceGroupArn: aws.String("arn:aws:resource-groups:us-west-2:123456789012:group/test-group"),
603+
LicenseConfigurationArns: []string{"arn:aws:license-manager:us-west-2:259732043995:license-configuration:lic-4acd3f7c117b9e314cce36e46084d071"},
604+
},
605+
},
606+
wantErr: false,
607+
},
496608
}
497609
for _, tt := range tests {
498610
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)