Skip to content

Commit 0b1f8a3

Browse files
authored
Support tweaking NodeGroupVersion update requests (#96)
Prior to this patch, the eks-controller wasn't able to set the ReleaseVersion, LaunchTemplate or the force attribute when making an `UpdateNodeGroupVersion` API Call. This patch correctly sets the ReleaseVersion and LaunchTemplate in the API request, and introduces a new annotation allowing users to set the Force attribute to true. We're adding a new annotation `eks.services.k8s.aws/force-update-version` - when set to true, the controller will toggle the Force boolean in the update request. Signed-off-by: Amine Hilaly <[email protected]> By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 1f8dfbb commit 0b1f8a3

File tree

4 files changed

+170
-5
lines changed

4 files changed

+170
-5
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Currently, the `eks-controller` is GA and supports the following resources:
1919
- `FargateProfile`
2020
- `Addon`
2121
- `PodIdentityAssociation`
22+
- `AccessEntry`
2223

2324
A detailed list of the resources supported specifications can be found in the
2425
[references][ack-references] section.
@@ -40,6 +41,11 @@ behavior of the controller. The following annotations are supported:
4041

4142
If not set, the controller will default to `ack-eks-controller`.
4243

44+
- `eks.services.k8s.aws/force-update-version`: used to force the version
45+
update of the nodegroup. If set to `true`, and the controller detects a
46+
a change in the nodegroup, it will set the `force` attribute to `true`
47+
in the `UpdateNodeGroupConfig` API call.
48+
4349
## Contributing
4450

4551
We welcome community contributions and pull requests.

apis/v1alpha1/annotation.go

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ var (
3434
// the value is not one of the above, the controller will default to managing the desired size
3535
// as if the annotation was set to "controller".
3636
DesiredSizeManagedByAnnotation = fmt.Sprintf("%s/desired-size-managed-by", GroupVersion.Group)
37+
// ForceNodeGroupUpdateVersionAnnotation is the annotation key used to force an update of the
38+
// nodegroup version. This annotation can only be set on a nodegroup custom resource.
39+
// The value of this annotation must be a boolean value. If the value is "true", the controller
40+
// will force an update of the nodegroup version to the value specified in the `version` field
41+
// of the `spec` object. If the value is "false", the controller will not force an update of the
42+
// nodegroup version.
43+
ForceNodeGroupUpdateVersionAnnotation = fmt.Sprintf("%s/force-update-version", GroupVersion.Group)
3744
)
3845

3946
const (

pkg/resource/nodegroup/hook.go

+52-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"context"
1818
"fmt"
1919
"reflect"
20+
"strconv"
2021
"time"
2122

2223
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
@@ -25,6 +26,7 @@ import (
2526
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
2627
svcsdk "github.com/aws/aws-sdk-go/service/eks"
2728
corev1 "k8s.io/api/core/v1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2830

2931
svcapitypes "github.com/aws-controllers-k8s/eks-controller/apis/v1alpha1"
3032
)
@@ -363,18 +365,40 @@ func newUpdateTaintsPayload(
363365
return &payload
364366
}
365367

368+
func newUpdateNodegroupVersionPayload(
369+
desired *resource,
370+
) *svcsdk.UpdateNodegroupVersionInput {
371+
input := &svcsdk.UpdateNodegroupVersionInput{
372+
NodegroupName: desired.ko.Spec.Name,
373+
ClusterName: desired.ko.Spec.ClusterName,
374+
Version: desired.ko.Spec.Version,
375+
ReleaseVersion: desired.ko.Spec.ReleaseVersion,
376+
}
377+
378+
if desired.ko.Spec.LaunchTemplate != nil {
379+
input.SetLaunchTemplate(&svcsdk.LaunchTemplateSpecification{
380+
Id: desired.ko.Spec.LaunchTemplate.ID,
381+
Name: desired.ko.Spec.LaunchTemplate.Name,
382+
Version: desired.ko.Spec.LaunchTemplate.Version,
383+
})
384+
}
385+
386+
if getUpdateNodeGroupForceAnnotation(desired.ko.ObjectMeta) {
387+
input.SetForce(true)
388+
}
389+
390+
return input
391+
}
392+
366393
func (rm *resourceManager) updateVersion(
367394
ctx context.Context,
368395
r *resource,
369396
) (err error) {
370397
rlog := ackrtlog.FromContext(ctx)
371398
exit := rlog.Trace("rm.updateVersion")
372399
defer exit(err)
373-
input := &svcsdk.UpdateNodegroupVersionInput{
374-
NodegroupName: r.ko.Spec.Name,
375-
ClusterName: r.ko.Spec.ClusterName,
376-
Version: r.ko.Spec.Version,
377-
}
400+
401+
input := newUpdateNodegroupVersionPayload(r)
378402

379403
_, err = rm.sdkapi.UpdateNodegroupVersionWithContext(ctx, input)
380404
rm.metrics.RecordAPICall("UPDATE", "UpdateNodegroupVersion", err)
@@ -385,6 +409,29 @@ func (rm *resourceManager) updateVersion(
385409
return nil
386410
}
387411

412+
const (
413+
defaultUpgradeNodeGroupVersion = false
414+
)
415+
416+
// GetDeleteForce returns whether the nodegroup should be deleted forcefully.
417+
//
418+
// https://docs.aws.amazon.com/eks/latest/APIReference/API_UpdateNodegroupVersion.html
419+
func getUpdateNodeGroupForceAnnotation(
420+
m metav1.ObjectMeta,
421+
) bool {
422+
resAnnotations := m.GetAnnotations()
423+
forceVersionUpdate, ok := resAnnotations[svcapitypes.ForceNodeGroupUpdateVersionAnnotation]
424+
if !ok {
425+
return defaultUpgradeNodeGroupVersion
426+
}
427+
428+
forceVersionUpdateBool, err := strconv.ParseBool(forceVersionUpdate)
429+
if err != nil {
430+
return defaultUpgradeNodeGroupVersion
431+
}
432+
433+
return forceVersionUpdateBool
434+
}
388435
func (rm *resourceManager) newUpdateScalingConfigPayload(
389436
desired, latest *resource,
390437
) *svcsdk.NodegroupScalingConfig {

pkg/resource/nodegroup/hook_test.go

+105
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,108 @@ func Test_resourceManager_newUpdateScalingConfigPayload_ManagedByDefault(t *test
422422
})
423423
}
424424
}
425+
426+
func Test_newUpdateNodegroupPayload(t *testing.T) {
427+
type args struct {
428+
r *resource
429+
}
430+
tests := []struct {
431+
name string
432+
args args
433+
wantVersion string
434+
wantForce bool
435+
wantLaunchTemplate bool
436+
}{
437+
{
438+
name: "version only",
439+
args: args{
440+
r: &resource{
441+
ko: &v1alpha1.Nodegroup{
442+
Spec: v1alpha1.NodegroupSpec{
443+
Version: aws.String("1.21"),
444+
},
445+
},
446+
},
447+
},
448+
wantVersion: "1.21",
449+
wantForce: false,
450+
wantLaunchTemplate: false,
451+
},
452+
{
453+
name: "all fields",
454+
args: args{
455+
r: &resource{
456+
ko: &v1alpha1.Nodegroup{
457+
Spec: v1alpha1.NodegroupSpec{
458+
Version: aws.String("1.21"),
459+
ReleaseVersion: aws.String("someversion"),
460+
LaunchTemplate: &v1alpha1.LaunchTemplateSpecification{
461+
ID: aws.String("id"),
462+
},
463+
},
464+
},
465+
},
466+
},
467+
wantVersion: "1.21",
468+
wantForce: false,
469+
wantLaunchTemplate: true,
470+
},
471+
{
472+
name: "force update annotation false",
473+
args: args{
474+
r: &resource{
475+
ko: &v1alpha1.Nodegroup{
476+
ObjectMeta: metav1.ObjectMeta{
477+
Annotations: map[string]string{
478+
"eks.services.k8s.aws/force-update-version": "false",
479+
},
480+
},
481+
Spec: v1alpha1.NodegroupSpec{
482+
Version: aws.String("1.21"),
483+
},
484+
},
485+
},
486+
},
487+
wantVersion: "1.21",
488+
wantForce: false,
489+
wantLaunchTemplate: false,
490+
},
491+
{
492+
name: "force update annotation true",
493+
args: args{
494+
r: &resource{
495+
ko: &v1alpha1.Nodegroup{
496+
ObjectMeta: metav1.ObjectMeta{
497+
Annotations: map[string]string{
498+
"eks.services.k8s.aws/force-update-version": "true",
499+
},
500+
},
501+
Spec: v1alpha1.NodegroupSpec{
502+
Version: aws.String("1.21"),
503+
},
504+
},
505+
},
506+
},
507+
wantVersion: "1.21",
508+
wantForce: true,
509+
wantLaunchTemplate: false,
510+
},
511+
}
512+
for _, tt := range tests {
513+
t.Run(tt.name, func(t *testing.T) {
514+
got := newUpdateNodegroupVersionPayload(tt.args.r)
515+
assert.Equal(t, tt.wantVersion, *got.Version)
516+
if tt.wantForce {
517+
assert.NotNil(t, got.Force)
518+
assert.True(t, *got.Force)
519+
} else {
520+
assert.Nil(t, got.Force)
521+
}
522+
if tt.wantLaunchTemplate {
523+
assert.NotNil(t, got.LaunchTemplate)
524+
} else {
525+
assert.Nil(t, got.LaunchTemplate)
526+
}
527+
})
528+
}
529+
}

0 commit comments

Comments
 (0)