Skip to content

Commit b5b2dc7

Browse files
committed
Support EKS upgrade policy
1 parent ffc8e3e commit b5b2dc7

File tree

10 files changed

+187
-0
lines changed

10 files changed

+187
-0
lines changed

config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,6 +3196,17 @@ spec:
31963196
- iam-authenticator
31973197
- aws-cli
31983198
type: string
3199+
upgradePolicy:
3200+
description: |-
3201+
The support policy to use for the cluster.
3202+
Extended support indicates that the cluster will not be automatically upgraded
3203+
when it leaves the standard support period, and will enter extended support.
3204+
Clusters in extended support have higher costs.
3205+
The default value is extended. Use standard to disable extended support.
3206+
enum:
3207+
- extended
3208+
- standard
3209+
type: string
31993210
version:
32003211
description: |-
32013212
Version defines the desired Kubernetes version. If no version number

controlplane/eks/api/v1beta1/conversion.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ func (r *AWSManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error {
121121
dst.Spec.RolePermissionsBoundary = restored.Spec.RolePermissionsBoundary
122122
dst.Status.Version = restored.Status.Version
123123
dst.Spec.BootstrapSelfManagedAddons = restored.Spec.BootstrapSelfManagedAddons
124+
dst.Spec.UpgradePolicy = restored.Spec.UpgradePolicy
124125
return nil
125126
}
126127

controlplane/eks/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.

controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@ type AWSManagedControlPlaneSpec struct { //nolint: maligned
208208

209209
// KubeProxy defines managed attributes of the kube-proxy daemonset
210210
KubeProxy KubeProxy `json:"kubeProxy,omitempty"`
211+
212+
// The support policy to use for the cluster.
213+
// Extended support indicates that the cluster will not be automatically upgraded
214+
// when it leaves the standard support period, and will enter extended support.
215+
// Clusters in extended support have higher costs.
216+
// The default value is extended. Use standard to disable extended support.
217+
// +kubebuilder:validation:Enum=extended;standard
218+
// +optional
219+
UpgradePolicy *UpgradePolicy `json:"upgradePolicy,omitempty"`
211220
}
212221

213222
// KubeProxy specifies how the kube-proxy daemonset is managed.

controlplane/eks/api/v1beta2/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,24 @@ type AddonIssue struct {
220220
ResourceIDs []string `json:"resourceIds,omitempty"`
221221
}
222222

223+
// UpgradePolicy defines the support policy to use for the cluster.
224+
type UpgradePolicy string
225+
226+
var (
227+
// UpgradePolicyExtended indicates that the cluster will not be automatically upgraded
228+
// when it leaves the standard support period, and will enter extended support.
229+
// Clusters in extended support have higher costs.
230+
UpgradePolicyExtended = UpgradePolicy("extended")
231+
232+
// UpgradePolicyStandard indicates that the cluster will be automatically upgraded
233+
// when it leaves the standard support period.
234+
UpgradePolicyStandard = UpgradePolicy("standard")
235+
)
236+
237+
func (e UpgradePolicy) String() string {
238+
return string(e)
239+
}
240+
223241
const (
224242
// SecurityGroupCluster is the security group for communication between EKS
225243
// control plane and managed node groups.

controlplane/eks/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.

docs/book/src/topics/eks/creating-a-cluster.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ clusterctl generate cluster capi-eks-quickstart --flavor eks-managedmachinepool
1414

1515
NOTE: When creating an EKS cluster only the **MAJOR.MINOR** of the `-kubernetes-version` is taken into consideration.
1616

17+
By default, EKS cluster uses:
18+
- EXTENDED support. See more info about [cluster upgrade policy](https://docs.aws.amazon.com/eks/latest/userguide/view-upgrade-policy.html)
19+
1720
## Kubeconfig
1821

1922
When creating an EKS cluster 2 kubeconfigs are generated and stored as secrets in the management cluster. This is different to when you create a non-managed cluster using the AWS provider.

pkg/cloud/converters/eks.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,11 @@ func AddonConflictResolutionFromSDK(conflict ekstypes.ResolveConflicts) *string
278278
}
279279
return aws.String(string(ekscontrolplanev1.AddonResolutionOverwrite))
280280
}
281+
282+
// SupportTypeToSDK converts CAPA upgrade support policy types to SDK types.
283+
func SupportTypeToSDK(input ekscontrolplanev1.UpgradePolicy) ekstypes.SupportType {
284+
if input == ekscontrolplanev1.UpgradePolicyStandard {
285+
return ekstypes.SupportTypeStandard
286+
}
287+
return ekstypes.SupportTypeExtended
288+
}

pkg/cloud/services/eks/cluster.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"net"
23+
"strings"
2324
"time"
2425

2526
"github.com/aws/aws-sdk-go-v2/aws"
@@ -35,6 +36,7 @@ import (
3536
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
3637
ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
3738
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/awserrors"
39+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/converters"
3840
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/wait"
3941
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/internal/cidr"
4042
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/internal/cmp"
@@ -460,6 +462,14 @@ func (s *Service) createCluster(ctx context.Context, eksClusterName string) (*ek
460462
eksVersion = &v
461463
}
462464

465+
var upgradePolicy *ekstypes.UpgradePolicyRequest
466+
467+
if s.scope.ControlPlane.Spec.UpgradePolicy != nil {
468+
upgradePolicy = &ekstypes.UpgradePolicyRequest{
469+
SupportType: converters.SupportTypeToSDK(*s.scope.ControlPlane.Spec.UpgradePolicy),
470+
}
471+
}
472+
463473
bootstrapAddon := s.scope.BootstrapSelfManagedAddons()
464474
input := &eks.CreateClusterInput{
465475
Name: aws.String(eksClusterName),
@@ -471,6 +481,7 @@ func (s *Service) createCluster(ctx context.Context, eksClusterName string) (*ek
471481
Tags: tags,
472482
KubernetesNetworkConfig: netConfig,
473483
BootstrapSelfManagedAddons: bootstrapAddon,
484+
UpgradePolicy: upgradePolicy,
474485
}
475486

476487
var out *eks.CreateClusterOutput
@@ -526,6 +537,12 @@ func (s *Service) reconcileClusterConfig(ctx context.Context, cluster *ekstypes.
526537
input.ResourcesVpcConfig = updateVpcConfig
527538
}
528539

540+
updateUpgradePolicy := s.reconcileUpgradePolicy(cluster.UpgradePolicy)
541+
if updateUpgradePolicy != nil {
542+
needsUpdate = true
543+
input.UpgradePolicy = updateUpgradePolicy
544+
}
545+
529546
if needsUpdate {
530547
if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
531548
if _, err := s.EKSClient.UpdateClusterConfig(ctx, input); err != nil {
@@ -719,6 +736,31 @@ func (s *Service) reconcileClusterVersion(ctx context.Context, cluster *ekstypes
719736
return nil
720737
}
721738

739+
func (s *Service) reconcileUpgradePolicy(upgradePolicy *ekstypes.UpgradePolicyResponse) *ekstypes.UpgradePolicyRequest {
740+
s.Info("reconciling upgrade policy")
741+
742+
if upgradePolicy == nil {
743+
s.Debug("cannot get cluster upgrade policy, no action")
744+
return nil
745+
}
746+
747+
clusterUpgradePolicy := upgradePolicy.SupportType
748+
749+
if s.scope.ControlPlane.Spec.UpgradePolicy == nil {
750+
s.Debug("upgrade policy not given, no action")
751+
return nil
752+
}
753+
754+
if strings.ToLower(string(clusterUpgradePolicy)) == s.scope.ControlPlane.Spec.UpgradePolicy.String() {
755+
s.Debug("upgrade policy unchanged, no action")
756+
return nil
757+
}
758+
759+
return &ekstypes.UpgradePolicyRequest{
760+
SupportType: converters.SupportTypeToSDK(*s.scope.ControlPlane.Spec.UpgradePolicy),
761+
}
762+
}
763+
722764
func (s *Service) describeEKSCluster(ctx context.Context, eksClusterName string) (*ekstypes.Cluster, error) {
723765
input := &eks.DescribeClusterInput{
724766
Name: aws.String(eksClusterName),

pkg/cloud/services/eks/cluster_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ func TestCreateCluster(t *testing.T) {
535535
RoleName: tc.role,
536536
NetworkSpec: infrav1.NetworkSpec{Subnets: tc.subnets},
537537
BootstrapSelfManagedAddons: false,
538+
UpgradePolicy: &ekscontrolplanev1.UpgradePolicyStandard,
538539
},
539540
},
540541
})
@@ -557,6 +558,9 @@ func TestCreateCluster(t *testing.T) {
557558
Tags: tc.tags,
558559
Version: version,
559560
BootstrapSelfManagedAddons: aws.Bool(false),
561+
UpgradePolicy: &ekstypes.UpgradePolicyRequest{
562+
SupportType: ekstypes.SupportTypeStandard,
563+
},
560564
}).Return(&eks.CreateClusterOutput{}, nil)
561565
}
562566
s := NewService(scope)
@@ -688,6 +692,91 @@ func TestReconcileEKSEncryptionConfig(t *testing.T) {
688692
}
689693
}
690694

695+
func TestReconcileUpgradePolicy(t *testing.T) {
696+
clusterName := "default.cluster"
697+
tests := []struct {
698+
name string
699+
oldUpgradePolicy *ekstypes.UpgradePolicyResponse
700+
newUpgradePolicy *ekscontrolplanev1.UpgradePolicy
701+
expect *ekstypes.UpgradePolicyRequest
702+
expectError bool
703+
}{
704+
{
705+
name: "no update necessary - no upgrade policy specified",
706+
oldUpgradePolicy: &ekstypes.UpgradePolicyResponse{
707+
SupportType: ekstypes.SupportTypeStandard,
708+
},
709+
expect: nil,
710+
expectError: false,
711+
},
712+
{
713+
name: "no update necessary - cannot get cluster upgrade policy",
714+
newUpgradePolicy: &ekscontrolplanev1.UpgradePolicyStandard,
715+
expect: nil,
716+
expectError: false,
717+
},
718+
{
719+
name: "no update necessary - upgrade policy unchanged",
720+
oldUpgradePolicy: &ekstypes.UpgradePolicyResponse{
721+
SupportType: ekstypes.SupportTypeStandard,
722+
},
723+
newUpgradePolicy: &ekscontrolplanev1.UpgradePolicyStandard,
724+
expect: nil,
725+
expectError: false,
726+
},
727+
{
728+
name: "needs update",
729+
oldUpgradePolicy: &ekstypes.UpgradePolicyResponse{
730+
SupportType: ekstypes.SupportTypeStandard,
731+
},
732+
newUpgradePolicy: &ekscontrolplanev1.UpgradePolicyExtended,
733+
expect: &ekstypes.UpgradePolicyRequest{
734+
SupportType: ekstypes.SupportTypeExtended,
735+
},
736+
expectError: false,
737+
},
738+
}
739+
740+
for _, tc := range tests {
741+
t.Run(tc.name, func(t *testing.T) {
742+
g := NewWithT(t)
743+
744+
mockControl := gomock.NewController(t)
745+
defer mockControl.Finish()
746+
747+
scheme := runtime.NewScheme()
748+
_ = infrav1.AddToScheme(scheme)
749+
_ = ekscontrolplanev1.AddToScheme(scheme)
750+
client := fake.NewClientBuilder().WithScheme(scheme).Build()
751+
scope, err := scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
752+
Client: client,
753+
Cluster: &clusterv1.Cluster{
754+
ObjectMeta: metav1.ObjectMeta{
755+
Namespace: "ns",
756+
Name: clusterName,
757+
},
758+
},
759+
ControlPlane: &ekscontrolplanev1.AWSManagedControlPlane{
760+
Spec: ekscontrolplanev1.AWSManagedControlPlaneSpec{
761+
Version: aws.String("1.16"),
762+
UpgradePolicy: tc.newUpgradePolicy,
763+
},
764+
},
765+
})
766+
g.Expect(err).To(BeNil())
767+
768+
s := NewService(scope)
769+
770+
upgradePolicyRequest := s.reconcileUpgradePolicy(tc.oldUpgradePolicy)
771+
if tc.expectError {
772+
g.Expect(err).To(HaveOccurred())
773+
return
774+
}
775+
g.Expect(upgradePolicyRequest).To(Equal(tc.expect))
776+
})
777+
}
778+
}
779+
691780
func TestCreateIPv6Cluster(t *testing.T) {
692781
g := NewWithT(t)
693782

0 commit comments

Comments
 (0)