From e4aba10e437cb61ccc459b1a303981b3cc78fccd Mon Sep 17 00:00:00 2001 From: Pahud Hsieh Date: Wed, 28 Aug 2024 11:44:11 -0400 Subject: [PATCH] fix(eks): can't update authMode with the same mode (#31043) The cluster resource handler would fail when updating the authMode with exactly the same mode. This could happen as described in https://github.com/aws/aws-cdk/issues/31032 We need to check if the cluster is already at the desired authMode and gracefully ignore the update. ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/31032 ### Reason for this change ### Description of changes ### Description of how you validated changes This PR is essentially to address a very special case described in https://github.com/aws/aws-cdk/issues/31032 and not easy to have a unit test or integ test for that. Instead, I validated it using manual deployment. step 1: initial deployment of a default eks cluster with undefined authenticationMode step 2: update the cluster and add a s3 bucket that would fail and trigger the rollback. At this point, eks auth mode would update but can't be rolled back. This makes the resource state out of sync with CFN. step 3: re-deploy the same stack without the s3 bucket but with the same auth mode in step 2. As the cluster has already modified its auth mode, this step should gracefully succeed. ```ts import { App, Stack, StackProps, aws_ec2 as ec2, aws_s3 as s3, } from 'aws-cdk-lib'; import * as eks from 'aws-cdk-lib/aws-eks'; import { getClusterVersionConfig } from './integ-tests-kubernetes-version'; interface EksClusterStackProps extends StackProps { authMode?: eks.AuthenticationMode; withFailedResource?: boolean; } class EksClusterStack extends Stack { constructor(scope: App, id: string, props?: EksClusterStackProps) { super(scope, id, { ...props, stackName: 'integ-eks-update-authmod', }); const vpc = new ec2.Vpc(this, 'Vpc', { maxAzs: 2, natGateways: 1, restrictDefaultSecurityGroup: false }); const cluster = new eks.Cluster(this, 'Cluster', { vpc, ...getClusterVersionConfig(this, eks.KubernetesVersion.V1_30), defaultCapacity: 0, authenticationMode: props?.authMode, }); if (props?.withFailedResource) { const bucket = new s3.Bucket(this, 'Bucket', { bucketName: 'aws' }); bucket.node.addDependency(cluster); } } } const app = new App(); // create a simple eks cluster for the initial deployment // new EksClusterStack(app, 'create-stack'); // 1st attempt to update with an intentional failure new EksClusterStack(app, 'update-stack', { authMode: eks.AuthenticationMode.API_AND_CONFIG_MAP, withFailedResource: true, }); // // 2nd attempt to update using the same authMode new EksClusterStack(app, 'update-stack', { authMode: eks.AuthenticationMode.API_AND_CONFIG_MAP, withFailedResource: false, }); ``` And it's validated in `us-east-1`. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/aws-eks/cluster-resource-handler/cluster.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/@aws-cdk/custom-resource-handlers/lib/aws-eks/cluster-resource-handler/cluster.ts b/packages/@aws-cdk/custom-resource-handlers/lib/aws-eks/cluster-resource-handler/cluster.ts index 93a5131ef4707..8afce971b41a1 100644 --- a/packages/@aws-cdk/custom-resource-handlers/lib/aws-eks/cluster-resource-handler/cluster.ts +++ b/packages/@aws-cdk/custom-resource-handlers/lib/aws-eks/cluster-resource-handler/cluster.ts @@ -247,6 +247,17 @@ export class ClusterResourceHandler extends ResourceHandler { this.newProps.accessConfig?.authenticationMode === 'API') { throw new Error('Cannot update from CONFIG_MAP to API'); } + // update-authmode will fail if we try to update to the same mode, + // so skip in this case. + try { + const cluster = (await this.eks.describeCluster({ name: this.clusterName })).cluster; + if (cluster?.accessConfig?.authenticationMode === this.newProps.accessConfig?.authenticationMode) { + console.log(`cluster already at ${cluster?.accessConfig?.authenticationMode}, skipping authMode update`); + return; + } + } catch (e: any) { + throw e; + } config.accessConfig = this.newProps.accessConfig; };