From 161e7af6803b58fea43ffdc3c6f9ee85cf658134 Mon Sep 17 00:00:00 2001 From: Ramon Quitales Date: Fri, 24 May 2024 14:37:10 -0400 Subject: [PATCH] fix: always set AMI ID for custom LaunchTemplates with ManagedNodeGroups (#1166) ### Proposed changes #1163 enabled setting custom user data in MIME format. However, AWS will attempt to merge the user data with its own copy if an AMI ID is not supplied in the LaunchTemplate. This results in the nodes being unable to join the cluster due to conflicting bootstrap script runs. This PR explicitly sets the ami id within the launch template. Testing: - Updated the existing tests to provision a cluster within a VPC to surface any node joining issues which CI was previously ignoring Fixes: #1165 --- examples/managed-nodegroups/index.ts | 52 ++++++++++++++++-------- examples/managed-nodegroups/package.json | 2 +- nodejs/eks/nodegroup.ts | 10 ++++- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/examples/managed-nodegroups/index.ts b/examples/managed-nodegroups/index.ts index d349ffb9a..fa65ccc68 100644 --- a/examples/managed-nodegroups/index.ts +++ b/examples/managed-nodegroups/index.ts @@ -1,4 +1,4 @@ -import * as aws from "@pulumi/aws"; +import * as awsx from "@pulumi/awsx"; import * as eks from "@pulumi/eks"; import * as iam from "./iam"; @@ -7,11 +7,22 @@ const role0 = iam.createRole("example-role0"); const role1 = iam.createRole("example-role1"); const role2 = iam.createRole("example-role2"); +// Create a new VPC +const eksVpc = new awsx.ec2.Vpc("eks-vpc", { + enableDnsHostnames: true, + cidrBlock: "10.0.0.0/16", +}); + // Create an EKS cluster. const cluster = new eks.Cluster("example-managed-nodegroups", { - skipDefaultNodeGroup: true, - deployDashboard: false, - instanceRoles: [role0, role1, role2], + skipDefaultNodeGroup: true, + deployDashboard: false, + vpcId: eksVpc.vpcId, + // Public subnets will be used for load balancers + publicSubnetIds: eksVpc.publicSubnetIds, + // Private subnets will be used for cluster nodes + privateSubnetIds: eksVpc.privateSubnetIds, + instanceRoles: [role0, role1, role2], }); // Export the cluster's kubeconfig. @@ -20,32 +31,41 @@ export const kubeconfig = cluster.kubeconfig; // Create a simple AWS managed node group using a cluster as input and the // refactored API. const managedNodeGroup0 = eks.createManagedNodeGroup("example-managed-ng0", { - cluster: cluster, - nodeRole: role0, - kubeletExtraArgs: "--max-pods=500", + cluster: cluster, + nodeRole: role0, + kubeletExtraArgs: "--max-pods=500", + enableIMDSv2: true, }); // Create a simple AWS managed node group using a cluster as input and the // initial API. -const managedNodeGroup1 = eks.createManagedNodeGroup("example-managed-ng1", { +const managedNodeGroup1 = eks.createManagedNodeGroup( + "example-managed-ng1", + { cluster: cluster, nodeGroupName: "aws-managed-ng1", nodeRoleArn: role1.arn, -}, cluster); + }, + cluster +); // Create an explicit AWS managed node group using a cluster as input and the // initial API. -const managedNodeGroup2 = eks.createManagedNodeGroup("example-managed-ng2", { +const managedNodeGroup2 = eks.createManagedNodeGroup( + "example-managed-ng2", + { cluster: cluster, nodeGroupName: "aws-managed-ng2", nodeRoleArn: role2.arn, scalingConfig: { - desiredSize: 1, - minSize: 1, - maxSize: 2, + desiredSize: 1, + minSize: 1, + maxSize: 2, }, diskSize: 20, instanceTypes: ["t3.medium"], - labels: {"ondemand": "true"}, - tags: {"org": "pulumi"}, -}, cluster); + labels: { ondemand: "true" }, + tags: { org: "pulumi" }, + }, + cluster +); diff --git a/examples/managed-nodegroups/package.json b/examples/managed-nodegroups/package.json index 101a6712f..c82f72618 100644 --- a/examples/managed-nodegroups/package.json +++ b/examples/managed-nodegroups/package.json @@ -5,7 +5,7 @@ "typescript": "^4.0.0" }, "dependencies": { - "@pulumi/aws": "^6.0.4", + "@pulumi/awsx": "^2.0.0", "@pulumi/eks": "latest", "@pulumi/pulumi": "^3.0.0" } diff --git a/nodejs/eks/nodegroup.ts b/nodejs/eks/nodegroup.ts index eed577566..9749a3240 100644 --- a/nodejs/eks/nodegroup.ts +++ b/nodejs/eks/nodegroup.ts @@ -1800,6 +1800,9 @@ Content-Type: text/x-shellscript; charset="us-ascii" { userData, metadataOptions, + // We need to always supply an imageId, otherwise AWS will attempt to merge the user data which will result in + // nodes failing to join the cluster. + imageId: getRecommendedAMI(args, core.cluster.version, parent), }, { parent, provider }, ); @@ -1812,11 +1815,14 @@ Content-Type: text/x-shellscript; charset="us-ascii" * See: https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html */ function getRecommendedAMI( - args: Omit | Omit, + args: Omit | Omit | Omit, k8sVersion: pulumi.Output, parent: pulumi.Resource | undefined, ): pulumi.Input { - const amiType = getAMIType(args.amiType, args.gpu, args.instanceType); + const gpu = "gpu" in args ? args.gpu : undefined; + const instanceType = "instanceType" in args ? args.instanceType : undefined; + + const amiType = getAMIType(args.amiType, gpu, instanceType); const amiID = k8sVersion.apply((v) => { const parameterName = `/aws/service/eks/optimized-ami/${v}/${amiType}/recommended/image_id`; return pulumi.output(aws.ssm.getParameter({ name: parameterName }, { parent, async: true }))