diff --git a/CHANGELOG.md b/CHANGELOG.md index ef0d195f..f8463bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Improvements +- feat(nodegroup): use the latest recommended AMIs from the SSM store + [#366](https://github.com/pulumi/pulumi-eks/pull/366) - feat(cluster): support HTTP(S) proxy for cluster readiness & OIDC config [#364](https://github.com/pulumi/pulumi-eks/pull/364) - deps(pulumi): bump node and go pulumi/pulumi to v1.13.1 diff --git a/nodejs/eks/cluster.ts b/nodejs/eks/cluster.ts index 4bd76d74..dfbfcb16 100644 --- a/nodejs/eks/cluster.ts +++ b/nodejs/eks/cluster.ts @@ -305,7 +305,8 @@ export function createCore(name: string, args: ClusterOptions, parent: pulumi.Co args.minSize || args.maxSize || args.desiredCapacity || - args.nodeAmiId)) { + args.nodeAmiId || + args.gpu)) { throw new Error("Setting nodeGroupOptions, and any set of singular node group option(s) on the cluster, is mutually exclusive. Choose a single approach."); } @@ -321,6 +322,7 @@ export function createCore(name: string, args: ClusterOptions, parent: pulumi.Co maxSize: args.maxSize, desiredCapacity: args.desiredCapacity, amiId: args.nodeAmiId, + gpu: args.gpu, version: args.version, }; @@ -880,12 +882,32 @@ export interface ClusterOptions { customInstanceRolePolicy?: pulumi.Input; /** - * The AMI to use for worker nodes. Defaults to the value of Amazon EKS - Optimized AMI if no value is provided. - * More information about the AWS eks optimized ami is available at https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html. - * Use the information provided by AWS if you want to build your own AMI. + * The AMI ID to use for the worker nodes. + * + * Defaults to the latest recommended EKS Optimized Linux AMI from the + * AWS Systems Manager Parameter Store. + * + * Note: `nodeAmiId` and `gpu` are mutually exclusive. + * + * See for more details: + * - https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html. */ nodeAmiId?: pulumi.Input; + /** + * Use the latest recommended EKS Optimized Linux AMI with GPU support for + * the worker nodes from the AWS Systems Manager Parameter Store. + * + * Defaults to false. + * + * Note: `gpu` and `nodeAmiId` are mutually exclusive. + * + * See for more details: + * - https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html. + * - https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html + */ + gpu?: pulumi.Input; + /** * Public key material for SSH access to worker nodes. See allowed formats at: * https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html diff --git a/nodejs/eks/examples/examples_test.go b/nodejs/eks/examples/examples_test.go index 93a1b7fc..456b6611 100644 --- a/nodejs/eks/examples/examples_test.go +++ b/nodejs/eks/examples/examples_test.go @@ -366,6 +366,21 @@ func TestAccTagInputTypes(t *testing.T) { integration.ProgramTest(t, &test) } +func TestAccNodegroupOptions(t *testing.T) { + test := getJSBaseOptions(t). + With(integration.ProgramTestOptions{ + Dir: path.Join(getCwd(t), "tests", "nodegroup-options"), + ExtraRuntimeValidation: func(t *testing.T, info integration.RuntimeValidationStackInfo) { + utils.RunEKSSmokeTest(t, + info.Deployment.Resources, + info.Outputs["kubeconfig"], + ) + }, + }) + + integration.ProgramTest(t, &test) +} + func TestAccMigrateNodeGroups(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") diff --git a/nodejs/eks/examples/tests/nodegroup-options/Pulumi.yaml b/nodejs/eks/examples/tests/nodegroup-options/Pulumi.yaml new file mode 100644 index 00000000..fe89502d --- /dev/null +++ b/nodejs/eks/examples/tests/nodegroup-options/Pulumi.yaml @@ -0,0 +1,3 @@ +name: test-nodegroup-options +description: Tests nodegroup options +runtime: nodejs diff --git a/nodejs/eks/examples/tests/nodegroup-options/README.md b/nodejs/eks/examples/tests/nodegroup-options/README.md new file mode 100755 index 00000000..f579fd71 --- /dev/null +++ b/nodejs/eks/examples/tests/nodegroup-options/README.md @@ -0,0 +1,3 @@ +# tests/nodegroup-options + +Tests nodegroup options: gpu. diff --git a/nodejs/eks/examples/tests/nodegroup-options/index.ts b/nodejs/eks/examples/tests/nodegroup-options/index.ts new file mode 100644 index 00000000..9c66603d --- /dev/null +++ b/nodejs/eks/examples/tests/nodegroup-options/index.ts @@ -0,0 +1,12 @@ +import * as eks from "@pulumi/eks"; +import * as pulumi from "@pulumi/pulumi"; + +const projectName = pulumi.getProject(); + +const cluster = new eks.Cluster(`${projectName}`, { + deployDashboard: false, + gpu: true, +}); + +// Export the cluster kubeconfig. +export const kubeconfig = cluster.kubeconfig; diff --git a/nodejs/eks/examples/tests/nodegroup-options/package.json b/nodejs/eks/examples/tests/nodegroup-options/package.json new file mode 100644 index 00000000..e1dee70a --- /dev/null +++ b/nodejs/eks/examples/tests/nodegroup-options/package.json @@ -0,0 +1,10 @@ +{ + "name": "example-nodegroup-options", + "devDependencies": { + "typescript": "^3.0.0" + }, + "dependencies": { + "@pulumi/pulumi": "^1.0.0", + "@pulumi/eks": "latest" + } +} diff --git a/nodejs/eks/examples/tests/nodegroup-options/tsconfig.json b/nodejs/eks/examples/tests/nodegroup-options/tsconfig.json new file mode 100644 index 00000000..2ea71672 --- /dev/null +++ b/nodejs/eks/examples/tests/nodegroup-options/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "outDir": "bin", + "target": "es6", + "lib": [ + "es6" + ], + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "stripInternal": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true, + "strictNullChecks": true + }, + "files": [ + "index.ts" + ] +} diff --git a/nodejs/eks/nodegroup.ts b/nodejs/eks/nodegroup.ts index 3c3bf3bc..9e9924e4 100644 --- a/nodejs/eks/nodegroup.ts +++ b/nodejs/eks/nodegroup.ts @@ -116,13 +116,32 @@ export interface NodeGroupBaseOptions { maxSize?: pulumi.Input; /** - * The AMI to use for worker nodes. Defaults to the current value of Amazon EKS - Optimized AMI at time of resource - * creation if no value is provided. More information about the AWS eks optimized ami is available at - * https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html. Use the information provided by AWS if - * you want to build your own AMI. + * The AMI ID to use for the worker nodes. + * + * Defaults to the latest recommended EKS Optimized Linux AMI from the + * AWS Systems Manager Parameter Store. + * + * Note: `amiId` and `gpu` are mutually exclusive. + * + * See for more details: + * - https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html. */ amiId?: pulumi.Input; + /** + * Use the latest recommended EKS Optimized Linux AMI with GPU support for + * the worker nodes from the AWS Systems Manager Parameter Store. + * + * Defaults to false. + * + * Note: `gpu` and `amiId` are mutually exclusive. + * + * See for more details: + * - https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html. + * - https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html + */ + gpu?: pulumi.Input; + /** * Custom k8s node labels to be attached to each woker node. Adds the given key/value pairs to the `--node-labels` * kubelet argument. @@ -291,6 +310,10 @@ export function createNodeGroup(name: string, args: NodeGroupOptions, parent: pu throw new Error("nodePublicKey and keyName are mutually exclusive. Choose a single approach"); } + if (args.amiId && args.gpu) { + throw new Error("amiId and gpu are mutually exclusive."); + } + let nodeSecurityGroup: aws.ec2.SecurityGroup; const cfnStackDeps: Array = []; @@ -391,59 +414,16 @@ ${customUserData} `; }); - let amiId: pulumi.Input = args.amiId!; - let ignoreChanges: string[] = []; - if (args.amiId === undefined) { - const version = pulumi.output(args.version || core.cluster.version); + const version = pulumi.output(args.version || core.cluster.version); + + // https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html + let amiId: pulumi.Input | undefined = args.amiId; + if (!amiId) { + const amiType = args.gpu ? "amazon-linux-2-gpu" : "amazon-linux-2"; amiId = version.apply(v => { - const filters: { name: string; values: string[] }[] = [ - // Filter to target Linux arch - { - name: "description", - values: ["*linux*", "*Linux*"], - }, - // Filter to target EKS Nodes - { - name: "name", - values: ["amazon-eks-node-*"], - }, - // Filter to target General, non-GPU image class - { - name: "manifest-location", - values: ["*amazon-eks-node*"], - }, - // Filter to target architecture - { - name: "architecture", - values: ["x86_64"], - }, - // Filter to target a specific k8s version - { - name: "description", - values: ["*k8s*" + v + "*"], - }, - ]; - - // `602401143452` is the ID assigned to `Amazon` and is required for AMIs that are hosted in AWS default regions - // `800184023465` is the owner ID of the creator of the EKS Optimized AMI in ap-east-1 - // `558608220178` is the owner ID of the creator of the EKS Optimized AMI in me-south-1 - // It is important to note that neither `800184023465` nor `558608220178` are assigned any IDs in default regions - // and `602401143452` is not assigned any AMIs in `ap-east-1` or `me-south-1` regions so this lookup is safe to make - // AWS Guide for EKS Optimized AMIs https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html - const eksWorkerAmiIds = aws.getAmiIds({ - filters, - owners: ["602401143452", "800184023465", "558608220178"], - sortAscending: true, - }, { parent, async: true }); - - const bestAmiId = eksWorkerAmiIds.then(r => r.ids.pop()!); - if (!bestAmiId) { - throw new Error("No Linux AMI Id was found."); - } - return bestAmiId; + const parameterName = `/aws/service/eks/optimized-ami/${v}/${amiType}/recommended/image_id`; + return aws.ssm.getParameter({name: parameterName}).value; }); - // When we automatically pick an image to use, we want to ignore any changes to this later by default. - ignoreChanges = ["imageId"]; } // Enable auto-assignment of public IP addresses on worker nodes for @@ -468,7 +448,7 @@ ${customUserData} deleteOnTermination: true, }, userData: userdata, - }, { parent: parent, ignoreChanges: ignoreChanges }); + }, { parent }); // Compute the worker node group subnets to use from the various approaches. let workerSubnetIds: pulumi.Output;