diff --git a/nodejs/eks/cluster/cluster.ts b/nodejs/eks/cluster/cluster.ts index 5c2721793..fabe7ee46 100644 --- a/nodejs/eks/cluster/cluster.ts +++ b/nodejs/eks/cluster/cluster.ts @@ -642,20 +642,43 @@ export function createCore( ); } - let computeConfig: pulumi.Input | undefined; + let computeConfig: pulumi.Output | undefined; if (args.autoMode?.enabled) { - computeConfig = pulumi.output(args.autoMode.computeConfig).apply((config) => { - return { - enabled: true, - nodeRoleArn: eksAutoNodeRole?.directRole.arn, - nodePools: ["general-purpose", "system"], - ...config, - }; - }); + computeConfig = pulumi + .all([args.autoMode.computeConfig, eksAutoNodeRole?.directRole.arn]) + .apply(([config, nodeRoleArn]) => { + return { + enabled: true, + nodeRoleArn, + nodePools: ["general-purpose", "system"], + ...config, + }; + }); } const storageConfig = args.autoMode?.enabled ? { blockStorage: { enabled: true } } : undefined; + // The upstream EKS cluster resource requires that all auto mode settings + // are either enabled or disabled and does not handle unknown values. + // Make all eks auto mode options depend on the other options to ensure + // that they're all either known or unknown when auto mode is enabled. + const autoModeConfig = + args.autoMode?.enabled && computeConfig && storageConfig && kubernetesNetworkConfig + ? pulumi + .all([computeConfig, storageConfig, kubernetesNetworkConfig]) + .apply(([computeConfig, storageConfig, kubernetesNetworkConfig]) => { + return { + computeConfig, + storageConfig, + kubernetesNetworkConfig, + }; + }) + : { + computeConfig, + storageConfig, + kubernetesNetworkConfig, + }; + // Create the EKS cluster const eksCluster = new aws.eks.Cluster( `${name}-eksCluster`, @@ -664,8 +687,9 @@ export function createCore( roleArn: args.serviceRole ? pulumi.output(args.serviceRole).arn : eksServiceRole?.directRole.arn!, - computeConfig, - storageConfig, + computeConfig: autoModeConfig.computeConfig, + storageConfig: autoModeConfig.storageConfig, + kubernetesNetworkConfig: autoModeConfig.kubernetesNetworkConfig, // When a cluster is created with EKS Auto Mode, it must be created without the addons bootstrapSelfManagedAddons: args.autoMode?.enabled ? false : undefined, vpcConfig: { @@ -701,7 +725,6 @@ export function createCore( }, ), encryptionConfig, - kubernetesNetworkConfig, accessConfig: args.authenticationMode ? { authenticationMode: args.authenticationMode, diff --git a/tests/nodejs_test.go b/tests/nodejs_test.go index 03dc3b422..24245b457 100644 --- a/tests/nodejs_test.go +++ b/tests/nodejs_test.go @@ -1118,6 +1118,45 @@ func TestAccEfa(t *testing.T) { programTestWithExtraOptions(t, &test, nil) } +func TestAccAutoModeCustomRole(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + test := getJSBaseOptions(t). + With(integration.ProgramTestOptions{ + Dir: path.Join(getTestPrograms(t), "auto-mode-custom-role"), + ExtraRuntimeValidation: func(t *testing.T, info integration.RuntimeValidationStackInfo) { + utils.ValidateClusters(t, info.Deployment.Resources, + utils.WithKubeConfigs(info.Outputs["kubeconfig"]), + utils.ValidateDaemonSets(), // no daemonsets in auto mode + utils.ValidateDeployments( + utils.KubernetesResource{Name: "nginx", Namespace: "nginx"}, + utils.KubernetesResource{Name: "coredns", Namespace: "kube-system"}, + ), + ) + }, + }) + + programTestWithExtraOptions(t, &test, nil) +} + +func TestAccAutoModePreview(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + cwd, err := os.Getwd() + require.NoError(t, err) + options := []opttest.Option{ + opttest.LocalProviderPath("eks", filepath.Join(cwd, "..", "bin")), + opttest.YarnLink("@pulumi/eks"), + } + pt := pulumitest.NewPulumiTest(t, path.Join(getTestPrograms(t), "auto-mode-preview"), options...) + + previewResult := pt.Preview(t) + t.Logf("Preview:\n%s", previewResult.StdOut) +} + func randomString(lenght int) string { const charset = "abcdefghijklmnopqrstuvwxyz0123456789" random := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) diff --git a/tests/testdata/programs/auto-mode-custom-role/Pulumi.yaml b/tests/testdata/programs/auto-mode-custom-role/Pulumi.yaml new file mode 100644 index 000000000..d288652d5 --- /dev/null +++ b/tests/testdata/programs/auto-mode-custom-role/Pulumi.yaml @@ -0,0 +1,3 @@ +name: auto-mode-custom-role +description: EKS Cluster in auto mode with custom node role +runtime: nodejs diff --git a/tests/testdata/programs/auto-mode-custom-role/index.ts b/tests/testdata/programs/auto-mode-custom-role/index.ts new file mode 100644 index 000000000..725257b02 --- /dev/null +++ b/tests/testdata/programs/auto-mode-custom-role/index.ts @@ -0,0 +1,51 @@ +import * as awsx from "@pulumi/awsx"; +import * as eks from "@pulumi/eks"; +import * as aws from "@pulumi/aws"; + +const eksVpc = new awsx.ec2.Vpc("auto-mode-custom-role", { + enableDnsHostnames: true, + cidrBlock: "10.0.0.0/16", + numberOfAvailabilityZones: 2, + subnetStrategy: "Auto", +}); + +const nodeRole = new aws.iam.Role("auto-mode-custom-role", { + assumeRolePolicy: aws.iam.getPolicyDocumentOutput({ + version: "2012-10-17", + statements: [{ + effect: "Allow", + principals: [{ + type: "Service", + identifiers: ["ec2.amazonaws.com"] + }], + actions: ["sts:AssumeRole", "sts:TagSession"] + }] + }).json +}); + +const attachments = [ + new aws.iam.RolePolicyAttachment("eks-node-role-policy-worker-node-minimal", { + role: nodeRole, + policyArn: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy", + }), + new aws.iam.RolePolicyAttachment("eks-node-role-policy-ecr-pull", { + role: nodeRole, + policyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly", + }), +]; + +const cluster = new eks.Cluster("auto-mode-custom-role", { + vpcId: eksVpc.vpcId, + authenticationMode: eks.AuthenticationMode.Api, + publicSubnetIds: eksVpc.publicSubnetIds, + privateSubnetIds: eksVpc.privateSubnetIds, + autoMode: { + enabled: true, + createNodeRole: false, + computeConfig: { + nodeRoleArn: nodeRole.arn, + } + } +}, { dependsOn: [...attachments] }); + +export const kubeconfig = cluster.kubeconfig; diff --git a/tests/testdata/programs/auto-mode-custom-role/package.json b/tests/testdata/programs/auto-mode-custom-role/package.json new file mode 100644 index 000000000..c288d2795 --- /dev/null +++ b/tests/testdata/programs/auto-mode-custom-role/package.json @@ -0,0 +1,12 @@ +{ + "name": "auto-mode-custom-role", + "devDependencies": { + "typescript": "^4.0.0", + "@types/node": "latest" + }, + "dependencies": { + "@pulumi/awsx": "^2.0.2", + "@pulumi/eks": "^3.0.0", + "@pulumi/pulumi": "^3.113.0" + } +} diff --git a/tests/testdata/programs/auto-mode-custom-role/tsconfig.json b/tests/testdata/programs/auto-mode-custom-role/tsconfig.json new file mode 100644 index 000000000..2ea71672d --- /dev/null +++ b/tests/testdata/programs/auto-mode-custom-role/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/tests/testdata/programs/auto-mode-preview/Pulumi.yaml b/tests/testdata/programs/auto-mode-preview/Pulumi.yaml new file mode 100644 index 000000000..bb2845ec5 --- /dev/null +++ b/tests/testdata/programs/auto-mode-preview/Pulumi.yaml @@ -0,0 +1,3 @@ +name: auto-mode-preview +description: EKS Cluster in auto mode +runtime: nodejs diff --git a/tests/testdata/programs/auto-mode-preview/index.ts b/tests/testdata/programs/auto-mode-preview/index.ts new file mode 100644 index 000000000..10b692b20 --- /dev/null +++ b/tests/testdata/programs/auto-mode-preview/index.ts @@ -0,0 +1,82 @@ +import * as awsx from "@pulumi/awsx"; +import * as eks from "@pulumi/eks"; +import * as aws from "@pulumi/aws"; +import * as random from "@pulumi/random"; + +const eksVpc = new awsx.ec2.Vpc("eks-vpc", { + enableDnsHostnames: true, + cidrBlock: "10.0.0.0/16", + numberOfAvailabilityZones: 2, + subnetStrategy: "Auto", +}); + +const nodeRole = new aws.iam.Role("eks-node-role", { + assumeRolePolicy: aws.iam.getPolicyDocumentOutput({ + version: "2012-10-17", + statements: [{ + effect: "Allow", + principals: [{ + type: "Service", + identifiers: ["ec2.amazonaws.com"] + }], + actions: ["sts:AssumeRole", "sts:TagSession"] + }] + }).json +}); + +const attachments = [ + new aws.iam.RolePolicyAttachment("eks-node-role-policy-worker-node-minimal", { + role: nodeRole, + policyArn: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy", + }), + new aws.iam.RolePolicyAttachment("eks-node-role-policy-ecr-pull", { + role: nodeRole, + policyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly", + }), +]; + +const randomId = new random.RandomId("random-id", { + byteLength: 8, +}); + +new eks.Cluster("eks-cluster-unknown-compute-config", { + vpcId: eksVpc.vpcId, + authenticationMode: eks.AuthenticationMode.Api, + publicSubnetIds: eksVpc.publicSubnetIds, + privateSubnetIds: eksVpc.privateSubnetIds, + autoMode: { + enabled: true, + createNodeRole: false, + computeConfig: { + nodeRoleArn: nodeRole.arn, + } + } +}, { dependsOn: [...attachments] }); + +new eks.Cluster("eks-cluster-unknown-ip-family", { + vpcId: eksVpc.vpcId, + authenticationMode: eks.AuthenticationMode.Api, + publicSubnetIds: eksVpc.publicSubnetIds, + privateSubnetIds: eksVpc.privateSubnetIds, + ipFamily: randomId.dec.apply(_ => "ipv4"), + autoMode: { + enabled: true, + } +}); + +new eks.Cluster("auto-mode", { + vpcId: eksVpc.vpcId, + authenticationMode: eks.AuthenticationMode.Api, + publicSubnetIds: eksVpc.publicSubnetIds, + privateSubnetIds: eksVpc.privateSubnetIds, + autoMode: { + enabled: true, + } +}); + +new eks.Cluster("regular-cluster", { + vpcId: eksVpc.vpcId, + authenticationMode: eks.AuthenticationMode.Api, + publicSubnetIds: eksVpc.publicSubnetIds, + privateSubnetIds: eksVpc.privateSubnetIds, +}); diff --git a/tests/testdata/programs/auto-mode-preview/package.json b/tests/testdata/programs/auto-mode-preview/package.json new file mode 100644 index 000000000..6bbc35261 --- /dev/null +++ b/tests/testdata/programs/auto-mode-preview/package.json @@ -0,0 +1,13 @@ +{ + "name": "auto-mode-preview", + "devDependencies": { + "typescript": "^4.0.0", + "@types/node": "latest" + }, + "dependencies": { + "@pulumi/awsx": "^2.0.2", + "@pulumi/eks": "^3.0.0", + "@pulumi/pulumi": "^3.113.0", + "@pulumi/random": "^4.17.0" + } +} diff --git a/tests/testdata/programs/auto-mode-preview/tsconfig.json b/tests/testdata/programs/auto-mode-preview/tsconfig.json new file mode 100644 index 000000000..2ea71672d --- /dev/null +++ b/tests/testdata/programs/auto-mode-preview/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" + ] +}