Skip to content

Commit ee5f9ee

Browse files
committed
Support setting maxHealthyPercentage to configure ASG instance refresh
1 parent 746c676 commit ee5f9ee

File tree

8 files changed

+87
-1
lines changed

8 files changed

+87
-1
lines changed

config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinepools.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,22 @@ spec:
969969
The default is to use the value for the health check grace period defined for the group.
970970
format: int64
971971
type: integer
972+
maxHealthyPercentage:
973+
description: |-
974+
The amount of capacity as a percentage in ASG that can be in service and healthy, or pending,
975+
to support your workload when replacing instances.
976+
The value is expressed as a percentage of the desired capacity of the ASG. Value range is 100 to 200.
977+
If you specify MaxHealthyPercentage , you must also specify MinHealthyPercentage , and the difference between
978+
them cannot be greater than 100.
979+
980+
981+
A larger range increases the number of instances that can be replaced at the same time.
982+
983+
984+
If you do not specify this property, the default is 100 percent, or the percentage set in the instance
985+
maintenance policy for the Auto Scaling group, if defined.
986+
format: int64
987+
type: integer
972988
minHealthyPercentage:
973989
description: |-
974990
The amount of capacity as a percentage in ASG that must remain healthy

exp/api/v1beta1/zz_generated.conversion.go

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

exp/api/v1beta2/awsmachinepool_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,19 @@ type RefreshPreferences struct {
172172
// during an instance refresh. The default is 90.
173173
// +optional
174174
MinHealthyPercentage *int64 `json:"minHealthyPercentage,omitempty"`
175+
176+
// The amount of capacity as a percentage in ASG that can be in service and healthy, or pending,
177+
// to support your workload when replacing instances.
178+
// The value is expressed as a percentage of the desired capacity of the ASG. Value range is 100 to 200.
179+
// If you specify MaxHealthyPercentage , you must also specify MinHealthyPercentage , and the difference between
180+
// them cannot be greater than 100.
181+
//
182+
// A larger range increases the number of instances that can be replaced at the same time.
183+
//
184+
// If you do not specify this property, the default is 100 percent, or the percentage set in the instance
185+
// maintenance policy for the Auto Scaling group, if defined.
186+
// +optional
187+
MaxHealthyPercentage *int64 `json:"maxHealthyPercentage,omitempty"`
175188
}
176189

177190
// AWSMachinePoolStatus defines the observed state of AWSMachinePool.

exp/api/v1beta2/awsmachinepool_webhook.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ func (r *AWSMachinePool) validateAdditionalSecurityGroups() field.ErrorList {
133133
}
134134
return allErrs
135135
}
136+
136137
func (r *AWSMachinePool) validateSpotInstances() field.ErrorList {
137138
var allErrs field.ErrorList
138139
if r.Spec.AWSLaunchTemplate.SpotMarketOptions != nil && r.Spec.MixedInstancesPolicy != nil {
@@ -141,6 +142,26 @@ func (r *AWSMachinePool) validateSpotInstances() field.ErrorList {
141142
return allErrs
142143
}
143144

145+
func (r *AWSMachinePool) validateRefreshPreferences() field.ErrorList {
146+
var allErrs field.ErrorList
147+
148+
if r.Spec.RefreshPreferences == nil {
149+
return allErrs
150+
}
151+
152+
if r.Spec.RefreshPreferences.MaxHealthyPercentage != nil && r.Spec.RefreshPreferences.MinHealthyPercentage == nil {
153+
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.refreshPreferences.maxHealthyPercentage"), "If you specify spec.refreshPreferences.maxHealthyPercentage, you must also specify spec.refreshPreferences.minHealthyPercentage"))
154+
}
155+
156+
if r.Spec.RefreshPreferences.MaxHealthyPercentage != nil && r.Spec.RefreshPreferences.MinHealthyPercentage != nil {
157+
if *r.Spec.RefreshPreferences.MaxHealthyPercentage-*r.Spec.RefreshPreferences.MinHealthyPercentage > 100 {
158+
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.refreshPreferences.maxHealthyPercentage"), "the difference between spec.refreshPreferences.maxHealthyPercentage and spec.refreshPreferences.minHealthyPercentage cannot be greater than 100"))
159+
}
160+
}
161+
162+
return allErrs
163+
}
164+
144165
// ValidateCreate will do any extra validation when creating a AWSMachinePool.
145166
func (r *AWSMachinePool) ValidateCreate() (admission.Warnings, error) {
146167
log.Info("AWSMachinePool validate create", "machine-pool", klog.KObj(r))
@@ -154,6 +175,7 @@ func (r *AWSMachinePool) ValidateCreate() (admission.Warnings, error) {
154175
allErrs = append(allErrs, r.validateSubnets()...)
155176
allErrs = append(allErrs, r.validateAdditionalSecurityGroups()...)
156177
allErrs = append(allErrs, r.validateSpotInstances()...)
178+
allErrs = append(allErrs, r.validateRefreshPreferences()...)
157179

158180
if len(allErrs) == 0 {
159181
return nil, nil
@@ -175,6 +197,7 @@ func (r *AWSMachinePool) ValidateUpdate(_ runtime.Object) (admission.Warnings, e
175197
allErrs = append(allErrs, r.validateSubnets()...)
176198
allErrs = append(allErrs, r.validateAdditionalSecurityGroups()...)
177199
allErrs = append(allErrs, r.validateSpotInstances()...)
200+
allErrs = append(allErrs, r.validateRefreshPreferences()...)
178201

179202
if len(allErrs) == 0 {
180203
return nil, nil

exp/api/v1beta2/awsmachinepool_webhook_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,27 @@ func TestAWSMachinePoolValidateCreate(t *testing.T) {
153153
},
154154
wantErr: true,
155155
},
156+
{
157+
name: "Should fail if MaxHealthyPercentage is set, but MinHealthyPercentage is not set",
158+
pool: &AWSMachinePool{
159+
Spec: AWSMachinePoolSpec{
160+
RefreshPreferences: &RefreshPreferences{MaxHealthyPercentage: aws.Int64(100)},
161+
},
162+
},
163+
wantErr: true,
164+
},
165+
{
166+
name: "Should fail if the difference between MaxHealthyPercentage and MinHealthyPercentage is greater than 100",
167+
pool: &AWSMachinePool{
168+
Spec: AWSMachinePoolSpec{
169+
RefreshPreferences: &RefreshPreferences{
170+
MaxHealthyPercentage: aws.Int64(150),
171+
MinHealthyPercentage: aws.Int64(25),
172+
},
173+
},
174+
},
175+
wantErr: true,
176+
},
156177
}
157178
for _, tt := range tests {
158179
t.Run(tt.name, func(t *testing.T) {

exp/api/v1beta2/zz_generated.deepcopy.go

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

pkg/cloud/services/autoscaling/autoscalinggroup.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func (s *Service) CanStartASGInstanceRefresh(scope *scope.MachinePoolScope) (boo
349349
// StartASGInstanceRefresh will start an ASG instance with refresh.
350350
func (s *Service) StartASGInstanceRefresh(scope *scope.MachinePoolScope) error {
351351
strategy := ptr.To[string](autoscaling.RefreshStrategyRolling)
352-
var minHealthyPercentage, instanceWarmup *int64
352+
var minHealthyPercentage, maxHealthyPercentage, instanceWarmup *int64
353353
if scope.AWSMachinePool.Spec.RefreshPreferences != nil {
354354
if scope.AWSMachinePool.Spec.RefreshPreferences.Strategy != nil {
355355
strategy = scope.AWSMachinePool.Spec.RefreshPreferences.Strategy
@@ -360,6 +360,9 @@ func (s *Service) StartASGInstanceRefresh(scope *scope.MachinePoolScope) error {
360360
if scope.AWSMachinePool.Spec.RefreshPreferences.MinHealthyPercentage != nil {
361361
minHealthyPercentage = scope.AWSMachinePool.Spec.RefreshPreferences.MinHealthyPercentage
362362
}
363+
if scope.AWSMachinePool.Spec.RefreshPreferences.MaxHealthyPercentage != nil {
364+
maxHealthyPercentage = scope.AWSMachinePool.Spec.RefreshPreferences.MaxHealthyPercentage
365+
}
363366
}
364367

365368
input := &autoscaling.StartInstanceRefreshInput{
@@ -368,6 +371,7 @@ func (s *Service) StartASGInstanceRefresh(scope *scope.MachinePoolScope) error {
368371
Preferences: &autoscaling.RefreshPreferences{
369372
InstanceWarmup: instanceWarmup,
370373
MinHealthyPercentage: minHealthyPercentage,
374+
MaxHealthyPercentage: maxHealthyPercentage,
371375
},
372376
}
373377

pkg/cloud/services/autoscaling/autoscalinggroup_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,7 @@ func TestServiceStartASGInstanceRefresh(t *testing.T) {
12031203
Preferences: &autoscaling.RefreshPreferences{
12041204
InstanceWarmup: aws.Int64(100),
12051205
MinHealthyPercentage: aws.Int64(80),
1206+
MaxHealthyPercentage: aws.Int64(100),
12061207
},
12071208
})).
12081209
Return(nil, awserrors.NewNotFound("not found"))
@@ -1218,6 +1219,7 @@ func TestServiceStartASGInstanceRefresh(t *testing.T) {
12181219
Preferences: &autoscaling.RefreshPreferences{
12191220
InstanceWarmup: aws.Int64(100),
12201221
MinHealthyPercentage: aws.Int64(80),
1222+
MaxHealthyPercentage: aws.Int64(100),
12211223
},
12221224
})).
12231225
Return(&autoscaling.StartInstanceRefreshOutput{}, nil)
@@ -1312,6 +1314,7 @@ func getMachinePoolScope(client client.Client, clusterScope *scope.ClusterScope)
13121314
Strategy: aws.String("Rolling"),
13131315
InstanceWarmup: aws.Int64(100),
13141316
MinHealthyPercentage: aws.Int64(80),
1317+
MaxHealthyPercentage: aws.Int64(100),
13151318
},
13161319
MixedInstancesPolicy: &expinfrav1.MixedInstancesPolicy{
13171320
InstancesDistribution: &expinfrav1.InstancesDistribution{

0 commit comments

Comments
 (0)