@pulumi/eks 2.8.1 → 3.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/addon.d.ts +61 -13
  2. package/addon.js +45 -18
  3. package/addon.js.map +1 -1
  4. package/cluster.d.ts +291 -585
  5. package/cluster.js +120 -947
  6. package/cluster.js.map +1 -1
  7. package/clusterCreationRoleProvider.d.ts +28 -0
  8. package/clusterCreationRoleProvider.js +47 -0
  9. package/clusterCreationRoleProvider.js.map +1 -0
  10. package/clusterMixins.d.ts +71 -0
  11. package/clusterMixins.js +107 -0
  12. package/clusterMixins.js.map +1 -0
  13. package/index.d.ts +31 -7
  14. package/index.js +80 -34
  15. package/index.js.map +1 -1
  16. package/managedNodeGroup.d.ts +221 -0
  17. package/managedNodeGroup.js +81 -0
  18. package/managedNodeGroup.js.map +1 -0
  19. package/nodeGroup.d.ts +273 -0
  20. package/nodeGroup.js +93 -0
  21. package/nodeGroup.js.map +1 -0
  22. package/nodeGroupSecurityGroup.d.ts +51 -0
  23. package/nodeGroupSecurityGroup.js +60 -0
  24. package/nodeGroupSecurityGroup.js.map +1 -0
  25. package/nodeGroupV2.d.ts +280 -0
  26. package/nodeGroupV2.js +90 -0
  27. package/nodeGroupV2.js.map +1 -0
  28. package/nodegroupMixins.d.ts +203 -0
  29. package/{securitygroup.js → nodegroupMixins.js} +25 -36
  30. package/nodegroupMixins.js.map +1 -0
  31. package/package.json +8 -36
  32. package/provider.d.ts +21 -0
  33. package/provider.js +38 -0
  34. package/provider.js.map +1 -0
  35. package/{storageclass.js → storageclassMixins.js} +1 -14
  36. package/storageclassMixins.js.map +1 -0
  37. package/types/enums/index.d.ts +170 -0
  38. package/types/enums/index.js +145 -0
  39. package/types/enums/index.js.map +1 -0
  40. package/types/index.d.ts +4 -0
  41. package/types/index.js +13 -0
  42. package/types/index.js.map +1 -0
  43. package/types/input.d.ts +745 -0
  44. package/types/input.js +30 -0
  45. package/types/input.js.map +1 -0
  46. package/types/output.d.ts +422 -0
  47. package/types/output.js +5 -0
  48. package/types/output.js.map +1 -0
  49. package/utilities.d.ts +8 -1
  50. package/utilities.js +90 -17
  51. package/utilities.js.map +1 -1
  52. package/vpcCniAddon.d.ts +175 -0
  53. package/vpcCniAddon.js +88 -0
  54. package/vpcCniAddon.js.map +1 -0
  55. package/LICENSE +0 -202
  56. package/README.md +0 -77
  57. package/authenticationMode.d.ts +0 -24
  58. package/authenticationMode.js +0 -172
  59. package/authenticationMode.js.map +0 -1
  60. package/authenticationMode.test.d.ts +0 -1
  61. package/authenticationMode.test.js +0 -208
  62. package/authenticationMode.test.js.map +0 -1
  63. package/cert-thumprint.d.ts +0 -16
  64. package/cert-thumprint.js +0 -113
  65. package/cert-thumprint.js.map +0 -1
  66. package/cmd/provider/addon.d.ts +0 -1
  67. package/cmd/provider/addon.js +0 -40
  68. package/cmd/provider/addon.js.map +0 -1
  69. package/cmd/provider/cluster.d.ts +0 -1
  70. package/cmd/provider/cluster.js +0 -71
  71. package/cmd/provider/cluster.js.map +0 -1
  72. package/cmd/provider/cni.d.ts +0 -2
  73. package/cmd/provider/cni.js +0 -291
  74. package/cmd/provider/cni.js.map +0 -1
  75. package/cmd/provider/index.d.ts +0 -1
  76. package/cmd/provider/index.js +0 -171
  77. package/cmd/provider/index.js.map +0 -1
  78. package/cmd/provider/nodegroup.d.ts +0 -1
  79. package/cmd/provider/nodegroup.js +0 -89
  80. package/cmd/provider/nodegroup.js.map +0 -1
  81. package/cmd/provider/randomSuffix.d.ts +0 -1
  82. package/cmd/provider/randomSuffix.js +0 -52
  83. package/cmd/provider/randomSuffix.js.map +0 -1
  84. package/cmd/provider/schema.json +0 -1909
  85. package/cmd/provider/securitygroup.d.ts +0 -1
  86. package/cmd/provider/securitygroup.js +0 -41
  87. package/cmd/provider/securitygroup.js.map +0 -1
  88. package/cni/README.md +0 -6
  89. package/cni/aws-k8s-cni.yaml +0 -602
  90. package/cni.d.ts +0 -177
  91. package/cni.js +0 -64
  92. package/cni.js.map +0 -1
  93. package/dashboard/heapster-rbac.yaml +0 -12
  94. package/dashboard/heapster.yaml +0 -46
  95. package/dashboard/influxdb.yaml +0 -40
  96. package/dashboard/kubernetes-dashboard.yaml +0 -167
  97. package/dashboard.d.ts +0 -5
  98. package/dashboard.js +0 -58
  99. package/dashboard.js.map +0 -1
  100. package/dependencies.d.ts +0 -2
  101. package/dependencies.js +0 -81
  102. package/dependencies.js.map +0 -1
  103. package/dependencies.test.d.ts +0 -1
  104. package/dependencies.test.js +0 -133
  105. package/dependencies.test.js.map +0 -1
  106. package/nodegroup.d.ts +0 -515
  107. package/nodegroup.js +0 -1152
  108. package/nodegroup.js.map +0 -1
  109. package/nodegroup.test.d.ts +0 -1
  110. package/nodegroup.test.js +0 -336
  111. package/nodegroup.test.js.map +0 -1
  112. package/package.json.dev +0 -67
  113. package/randomSuffix.d.ts +0 -1
  114. package/randomSuffix.js +0 -51
  115. package/randomSuffix.js.map +0 -1
  116. package/securitygroup.d.ts +0 -52
  117. package/securitygroup.js.map +0 -1
  118. package/servicerole.d.ts +0 -43
  119. package/servicerole.js +0 -72
  120. package/servicerole.js.map +0 -1
  121. package/storageclass.js.map +0 -1
  122. package/utils.d.ts +0 -23
  123. package/utils.js +0 -16
  124. package/utils.js.map +0 -1
  125. /package/{storageclass.d.ts → storageclassMixins.d.ts} +0 -0
package/nodegroup.js DELETED
@@ -1,1152 +0,0 @@
1
- "use strict";
2
- // Copyright 2016-2022, Pulumi Corporation.
3
- //
4
- // Licensed under the Apache License, Version 2.0 (the "License");
5
- // you may not use this file except in compliance with the License.
6
- // You may obtain a copy of the License at
7
- //
8
- // http://www.apache.org/licenses/LICENSE-2.0
9
- //
10
- // Unless required by applicable law or agreed to in writing, software
11
- // distributed under the License is distributed on an "AS IS" BASIS,
12
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- // See the License for the specific language governing permissions and
14
- // limitations under the License.
15
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
17
- return new (P || (P = Promise))(function (resolve, reject) {
18
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
19
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
20
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21
- step((generator = generator.apply(thisArg, _arguments || [])).next());
22
- });
23
- };
24
- Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.getArchitecture = exports.isGravitonInstance = exports.createManagedNodeGroup = exports.ManagedNodeGroupInternal = exports.ManagedNodeGroup = exports.computeWorkerSubnets = exports.createNodeGroupV2 = exports.createNodeGroup = exports.NodeGroupV2Internal = exports.NodeGroupV2 = exports.NodeGroupInternal = exports.NodeGroup = void 0;
26
- const aws = require("@pulumi/aws");
27
- const pulumi = require("@pulumi/pulumi");
28
- const netmask = require("netmask");
29
- const authenticationMode_1 = require("./authenticationMode");
30
- const cluster_1 = require("./cluster");
31
- const randomSuffix_1 = require("./randomSuffix");
32
- const securitygroup_1 = require("./securitygroup");
33
- /**
34
- * NodeGroup is a component that wraps the AWS EC2 instances that provide compute capacity for an EKS cluster.
35
- */
36
- class NodeGroup extends pulumi.ComponentResource {
37
- /**
38
- * Create a new EKS cluster with worker nodes, optional storage classes, and deploy the Kubernetes Dashboard if
39
- * requested.
40
- *
41
- * @param name The _unique_ name of this component.
42
- * @param args The arguments for this cluster.
43
- * @param opts A bag of options that control this component's behavior.
44
- */
45
- constructor(name, args, opts) {
46
- super("eks:index:NodeGroup", name, args, opts);
47
- const group = createNodeGroup(name, args, this, opts === null || opts === void 0 ? void 0 : opts.provider);
48
- this.nodeSecurityGroup = group.nodeSecurityGroup;
49
- this.cfnStack = group.cfnStack;
50
- this.autoScalingGroupName = group.autoScalingGroupName;
51
- this.registerOutputs(undefined);
52
- }
53
- }
54
- exports.NodeGroup = NodeGroup;
55
- /**
56
- * This is a variant of `NodeGroup` that is used for the MLC `NodeGroup`. We don't just use `NodeGroup`,
57
- * because we need to accept `ClusterInternal` as the `cluster` arg, so we can correctly pull out `cluster.core`
58
- * for use in creating the `NodeGroup`.
59
- *
60
- * @internal
61
- */
62
- class NodeGroupInternal extends pulumi.ComponentResource {
63
- constructor(name, args, opts) {
64
- var _a;
65
- const type = "eks:index:NodeGroup";
66
- if (opts === null || opts === void 0 ? void 0 : opts.urn) {
67
- const props = {
68
- autoScalingGroupName: undefined,
69
- cfnStack: undefined,
70
- extraNodeSecurityGroups: undefined,
71
- nodeSecurityGroup: undefined,
72
- };
73
- super(type, name, props, opts);
74
- return;
75
- }
76
- super(type, name, args, opts);
77
- const core = pulumi
78
- .output(args.cluster)
79
- .apply((c) => (c instanceof cluster_1.ClusterInternal ? c.core : c));
80
- const group = createNodeGroupInternal(name, args, core, this, opts === null || opts === void 0 ? void 0 : opts.provider);
81
- this.autoScalingGroupName = group.autoScalingGroupName;
82
- this.cfnStack = pulumi.output(group.cfnStack);
83
- this.extraNodeSecurityGroups = pulumi.output((_a = group.extraNodeSecurityGroups) !== null && _a !== void 0 ? _a : []);
84
- this.nodeSecurityGroup = pulumi.output(group.nodeSecurityGroup);
85
- this.registerOutputs({
86
- autoScalingGroupName: this.autoScalingGroupName,
87
- cfnStack: this.cfnStack,
88
- extraNodeSecurityGroups: this.extraNodeSecurityGroups,
89
- nodeSecurityGroup: this.nodeSecurityGroup,
90
- });
91
- }
92
- }
93
- exports.NodeGroupInternal = NodeGroupInternal;
94
- class NodeGroupV2 extends pulumi.ComponentResource {
95
- /**
96
- * Create a new EKS cluster with worker nodes, optional storage classes, and deploy the Kubernetes Dashboard if
97
- * requested.
98
- *
99
- * @param name The _unique_ name of this component.
100
- * @param args The arguments for this cluster.
101
- * @param opts A bag of options that control this component's behavior.
102
- */
103
- constructor(name, args, opts) {
104
- super("eks:index:NodeGroupV2", name, args, opts);
105
- const group = createNodeGroupV2(name, args, this, opts === null || opts === void 0 ? void 0 : opts.provider);
106
- this.nodeSecurityGroup = group.nodeSecurityGroup;
107
- this.autoScalingGroup = group.autoScalingGroup;
108
- this.registerOutputs(undefined);
109
- }
110
- }
111
- exports.NodeGroupV2 = NodeGroupV2;
112
- /**
113
- * This is a variant of `NodeGroupV2` that is used for the MLC `NodeGroupV2`. We don't just use `NodeGroupV2`,
114
- * because we need to accept `ClusterInternal` as the `cluster` arg, so we can correctly pull out `cluster.core`
115
- * for use in creating the `NodeGroupV2`.
116
- *
117
- * @internal
118
- */
119
- class NodeGroupV2Internal extends pulumi.ComponentResource {
120
- constructor(name, args, opts) {
121
- var _a;
122
- const type = "eks:index:NodeGroupV2";
123
- if (opts === null || opts === void 0 ? void 0 : opts.urn) {
124
- const props = {
125
- autoScalingGroup: undefined,
126
- extraNodeSecurityGroups: undefined,
127
- nodeSecurityGroup: undefined,
128
- };
129
- super(type, name, props, opts);
130
- return;
131
- }
132
- super(type, name, args, opts);
133
- const core = pulumi
134
- .output(args.cluster)
135
- .apply((c) => (c instanceof cluster_1.ClusterInternal ? c.core : c));
136
- const group = createNodeGroupV2Internal(name, args, core, this, opts === null || opts === void 0 ? void 0 : opts.provider);
137
- this.autoScalingGroup = pulumi.output(group.autoScalingGroup);
138
- this.extraNodeSecurityGroups = pulumi.output((_a = group.extraNodeSecurityGroups) !== null && _a !== void 0 ? _a : []);
139
- this.nodeSecurityGroup = pulumi.output(group.nodeSecurityGroup);
140
- this.registerOutputs({
141
- autoScalingGroup: this.autoScalingGroup,
142
- extraNodeSecurityGroups: this.extraNodeSecurityGroups,
143
- nodeSecurityGroup: this.nodeSecurityGroup,
144
- });
145
- }
146
- }
147
- exports.NodeGroupV2Internal = NodeGroupV2Internal;
148
- /**
149
- * Create a self-managed node group using CloudFormation and an ASG.
150
- *
151
- * See for more details:
152
- * https://docs.aws.amazon.com/eks/latest/userguide/worker.html
153
- */
154
- function createNodeGroup(name, args, parent, provider) {
155
- const core = args.cluster instanceof cluster_1.Cluster ? args.cluster.core : args.cluster;
156
- return createNodeGroupInternal(name, args, pulumi.output(core), parent, provider);
157
- }
158
- exports.createNodeGroup = createNodeGroup;
159
- function createNodeGroupInternal(name, args, core, parent, provider) {
160
- var _a, _b, _c, _d;
161
- const instanceProfile = core.apply((c) => {
162
- var _a;
163
- if (!args.instanceProfile && !c.nodeGroupOptions.instanceProfile) {
164
- throw new pulumi.ResourceError(`an instanceProfile is required`, parent);
165
- }
166
- return (_a = args.instanceProfile) !== null && _a !== void 0 ? _a : c.nodeGroupOptions.instanceProfile;
167
- });
168
- core.apply((c) => {
169
- if (c.nodeGroupOptions.nodeSecurityGroup && args.nodeSecurityGroup) {
170
- if (c.nodeSecurityGroupTags &&
171
- c.nodeGroupOptions.nodeSecurityGroup.id !== args.nodeSecurityGroup.id) {
172
- throw new pulumi.ResourceError(`The NodeGroup's nodeSecurityGroup and the cluster option nodeSecurityGroupTags are mutually exclusive. Choose a single approach`, parent);
173
- }
174
- }
175
- });
176
- if (args.nodePublicKey && args.keyName) {
177
- throw new pulumi.ResourceError("nodePublicKey and keyName are mutually exclusive. Choose a single approach", parent);
178
- }
179
- if (args.amiId && args.gpu) {
180
- throw new pulumi.ResourceError("amiId and gpu are mutually exclusive.", parent);
181
- }
182
- if (args.nodeUserDataOverride &&
183
- (args.nodeUserData ||
184
- args.labels ||
185
- args.taints ||
186
- args.kubeletExtraArgs ||
187
- args.bootstrapExtraArgs)) {
188
- throw new pulumi.ResourceError("nodeUserDataOverride and any combination of {nodeUserData, labels, taints, kubeletExtraArgs, or bootstrapExtraArgs} is mutually exclusive.", parent);
189
- }
190
- let nodeSecurityGroup;
191
- const eksCluster = core.cluster;
192
- const cfnStackDeps = core.apply((c) => {
193
- const result = [];
194
- if (c.vpcCni !== undefined) {
195
- result.push(c.vpcCni);
196
- }
197
- if (c.eksNodeAccess !== undefined) {
198
- result.push(c.eksNodeAccess);
199
- }
200
- return result;
201
- });
202
- let eksClusterIngressRule = args.clusterIngressRule;
203
- if (args.nodeSecurityGroup) {
204
- nodeSecurityGroup = args.nodeSecurityGroup;
205
- if (eksClusterIngressRule === undefined) {
206
- throw new pulumi.ResourceError(`invalid args for node group ${name}, clusterIngressRule is required when nodeSecurityGroup is manually specified`, parent);
207
- }
208
- }
209
- else {
210
- [nodeSecurityGroup, eksClusterIngressRule] = (0, securitygroup_1.createNodeGroupSecurityGroup)(name, {
211
- vpcId: core.vpcId,
212
- clusterSecurityGroup: core.clusterSecurityGroup,
213
- eksCluster: eksCluster,
214
- tags: pulumi.all([core.tags, core.nodeSecurityGroupTags]).apply(([tags, nodeSecurityGroupTags]) => (Object.assign(Object.assign({}, nodeSecurityGroupTags), tags))),
215
- }, parent);
216
- }
217
- // This apply is necessary in s.t. the launchConfiguration picks up a
218
- // dependency on the eksClusterIngressRule. The nodes may fail to
219
- // connect to the cluster if we attempt to create them before the
220
- // ingress rule is applied.
221
- const nodeSecurityGroupId = pulumi
222
- .all([nodeSecurityGroup.id, eksClusterIngressRule.id])
223
- .apply(([id]) => id);
224
- // Collect the IDs of any extra, user-specific security groups.
225
- const extraSGOutput = pulumi.output(args.extraNodeSecurityGroups);
226
- const extraNodeSecurityGroupIds = pulumi.all([extraSGOutput]).apply(([sg]) => {
227
- if (sg === undefined) {
228
- return [];
229
- }
230
- // Map out the ARNs of all of the instanceRoles.
231
- return sg.map((sg) => sg.id);
232
- });
233
- // If requested, add a new EC2 KeyPair for SSH access to the instances.
234
- let keyName = args.keyName;
235
- if (args.nodePublicKey) {
236
- const key = new aws.ec2.KeyPair(`${name}-keyPair`, {
237
- publicKey: args.nodePublicKey,
238
- }, { parent, provider });
239
- keyName = key.keyName;
240
- }
241
- const cfnStackName = (0, randomSuffix_1.default)(`${name}-cfnStackName`, name, { parent });
242
- const awsRegion = pulumi.output(aws.getRegion({}, { parent, async: true }));
243
- const userDataArg = args.nodeUserData || pulumi.output("");
244
- const kubeletExtraArgs = args.kubeletExtraArgs ? args.kubeletExtraArgs.split(" ") : [];
245
- if (args.labels) {
246
- const parts = [];
247
- for (const key of Object.keys(args.labels)) {
248
- parts.push(key + "=" + args.labels[key]);
249
- }
250
- if (parts.length > 0) {
251
- kubeletExtraArgs.push("--node-labels=" + parts.join(","));
252
- }
253
- }
254
- if (args.taints) {
255
- const parts = [];
256
- for (const key of Object.keys(args.taints)) {
257
- const taint = args.taints[key];
258
- parts.push(key + "=" + taint.value + ":" + taint.effect);
259
- }
260
- if (parts.length > 0) {
261
- kubeletExtraArgs.push("--register-with-taints=" + parts.join(","));
262
- }
263
- }
264
- let bootstrapExtraArgs = args.bootstrapExtraArgs ? " " + args.bootstrapExtraArgs : "";
265
- if (kubeletExtraArgs.length === 1) {
266
- // For backward compatibility with previous versions of this package, don't wrap a single argument with `''`.
267
- bootstrapExtraArgs += ` --kubelet-extra-args ${kubeletExtraArgs[0]}`;
268
- }
269
- else if (kubeletExtraArgs.length > 1) {
270
- bootstrapExtraArgs += ` --kubelet-extra-args '${kubeletExtraArgs.join(" ")}'`;
271
- }
272
- const userdata = pulumi
273
- .all([
274
- awsRegion,
275
- eksCluster.name,
276
- eksCluster.endpoint,
277
- eksCluster.certificateAuthority,
278
- cfnStackName,
279
- userDataArg,
280
- ])
281
- .apply(([region, clusterName, clusterEndpoint, clusterCa, stackName, customUserData]) => {
282
- if (customUserData !== "") {
283
- customUserData = `cat >/opt/user-data <<${stackName}-user-data
284
- ${customUserData}
285
- ${stackName}-user-data
286
- chmod +x /opt/user-data
287
- /opt/user-data
288
- `;
289
- }
290
- return `#!/bin/bash
291
-
292
- /etc/eks/bootstrap.sh --apiserver-endpoint "${clusterEndpoint}" --b64-cluster-ca "${clusterCa.data}" "${clusterName}"${bootstrapExtraArgs}
293
- ${customUserData}
294
- /opt/aws/bin/cfn-signal --exit-code $? --stack ${stackName} --resource NodeGroup --region ${region.name}
295
- `;
296
- });
297
- const version = pulumi.output(args.version || core.cluster.version);
298
- const amiId = args.amiId || getRecommendedAMI(args, version, parent);
299
- // Enable auto-assignment of public IP addresses on worker nodes for
300
- // backwards compatibility on existing EKS clusters launched with it
301
- // enabled. Defaults to `true`.
302
- let nodeAssociatePublicIpAddress = true;
303
- if (args.nodeAssociatePublicIpAddress !== undefined) {
304
- nodeAssociatePublicIpAddress = args.nodeAssociatePublicIpAddress;
305
- }
306
- const numeric = new RegExp("^\\d+$");
307
- // We need to wrap the validation in a pulumi.all as MLCs could supply pulumi.Output<T> or T.
308
- pulumi
309
- .all([args.nodeRootVolumeIops, args.nodeRootVolumeType, args.nodeRootVolumeThroughput])
310
- .apply(([nodeRootVolumeIops, nodeRootVolumeType, nodeRootVolumeThroughput]) => {
311
- if (nodeRootVolumeIops && nodeRootVolumeType !== "io1") {
312
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of non-io1 type with provisioned IOPS (nodeRootVolumeIops).", parent);
313
- }
314
- if (nodeRootVolumeType === "io1" && nodeRootVolumeIops) {
315
- if (!numeric.test(nodeRootVolumeIops === null || nodeRootVolumeIops === void 0 ? void 0 : nodeRootVolumeIops.toString())) {
316
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of io1 type without provisioned IOPS (nodeRootVolumeIops) as integer value.", parent);
317
- }
318
- }
319
- if (nodeRootVolumeThroughput && nodeRootVolumeType !== "gp3") {
320
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of non-gp3 type with provisioned throughput (nodeRootVolumeThroughput).", parent);
321
- }
322
- if (nodeRootVolumeType === "gp3" && nodeRootVolumeThroughput) {
323
- if (!numeric.test(nodeRootVolumeThroughput === null || nodeRootVolumeThroughput === void 0 ? void 0 : nodeRootVolumeThroughput.toString())) {
324
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of gp3 type without provisioned throughput (nodeRootVolumeThroughput) as integer value.", parent);
325
- }
326
- }
327
- });
328
- const nodeLaunchConfiguration = new aws.ec2.LaunchConfiguration(`${name}-nodeLaunchConfiguration`, {
329
- associatePublicIpAddress: nodeAssociatePublicIpAddress,
330
- imageId: amiId,
331
- instanceType: args.instanceType || "t2.medium",
332
- iamInstanceProfile: instanceProfile,
333
- keyName: keyName,
334
- securityGroups: pulumi
335
- .all([nodeSecurityGroupId, extraNodeSecurityGroupIds])
336
- .apply(([sg, extraSG]) => [sg, ...extraSG]),
337
- spotPrice: args.spotPrice,
338
- rootBlockDevice: {
339
- encrypted: (_a = args.nodeRootVolumeEncrypted) !== null && _a !== void 0 ? _a : false,
340
- volumeSize: (_b = args.nodeRootVolumeSize) !== null && _b !== void 0 ? _b : 20,
341
- volumeType: (_c = args.nodeRootVolumeType) !== null && _c !== void 0 ? _c : "gp2",
342
- iops: args.nodeRootVolumeIops,
343
- throughput: args.nodeRootVolumeThroughput,
344
- deleteOnTermination: (_d = args.nodeRootVolumeDeleteOnTermination) !== null && _d !== void 0 ? _d : true,
345
- },
346
- userData: args.nodeUserDataOverride || userdata,
347
- enableMonitoring: args.enableDetailedMonitoring,
348
- }, { parent, provider });
349
- // Compute the worker node group subnets to use from the various approaches.
350
- let workerSubnetIds;
351
- if (args.nodeSubnetIds !== undefined) {
352
- // Use the specified override subnetIds.
353
- workerSubnetIds = pulumi.output(args.nodeSubnetIds);
354
- }
355
- else {
356
- workerSubnetIds = core.apply((c) => {
357
- if (c.privateSubnetIds !== undefined) {
358
- // Use the specified private subnetIds.
359
- return Promise.resolve(c.privateSubnetIds);
360
- }
361
- else if (c.publicSubnetIds !== undefined) {
362
- // Use the specified public subnetIds.
363
- return Promise.resolve(c.publicSubnetIds);
364
- }
365
- else {
366
- // Use subnetIds from the cluster. Compute / auto-discover the private worker subnetIds from this set.
367
- return computeWorkerSubnets(parent, c.subnetIds);
368
- }
369
- });
370
- }
371
- // Configure the settings for the autoscaling group.
372
- if (args.desiredCapacity === undefined) {
373
- args.desiredCapacity = 2;
374
- }
375
- if (args.minSize === undefined) {
376
- args.minSize = 1;
377
- }
378
- if (args.maxSize === undefined) {
379
- args.maxSize = 2;
380
- }
381
- let minInstancesInService = 1;
382
- if (args.spotPrice) {
383
- minInstancesInService = 0;
384
- }
385
- const autoScalingGroupTags = pulumi
386
- .all([eksCluster.name, args.autoScalingGroupTags])
387
- .apply(([clusterName, asgTags]) => (Object.assign({ Name: `${clusterName}-worker`, [`kubernetes.io/cluster/${clusterName}`]: "owned" }, asgTags)));
388
- const cfnTemplateBody = pulumi
389
- .all([
390
- nodeLaunchConfiguration.id,
391
- args.desiredCapacity,
392
- args.minSize,
393
- args.maxSize,
394
- tagsToAsgTags(autoScalingGroupTags),
395
- workerSubnetIds.apply(JSON.stringify),
396
- ])
397
- .apply(([launchConfig, desiredCapacity, minSize, maxSize, asgTags, vpcSubnetIds]) => `
398
- AWSTemplateFormatVersion: '2010-09-09'
399
- Outputs:
400
- NodeGroup:
401
- Value: !Ref NodeGroup
402
- Resources:
403
- NodeGroup:
404
- Type: AWS::AutoScaling::AutoScalingGroup
405
- Properties:
406
- DesiredCapacity: ${desiredCapacity}
407
- LaunchConfigurationName: ${launchConfig}
408
- MinSize: ${minSize}
409
- MaxSize: ${maxSize}
410
- VPCZoneIdentifier: ${vpcSubnetIds}
411
- Tags:
412
- ${asgTags}
413
- UpdatePolicy:
414
- AutoScalingRollingUpdate:
415
- MinInstancesInService: '${minInstancesInService}'
416
- MaxBatchSize: '1'
417
- `);
418
- const cfnStack = new aws.cloudformation.Stack(`${name}-nodes`, {
419
- name: cfnStackName,
420
- templateBody: cfnTemplateBody,
421
- tags: pulumi.all([core.tags, args.cloudFormationTags]).apply(([tags, cloudFormationTags]) => (Object.assign(Object.assign({ Name: `${name}-nodes` }, cloudFormationTags), tags))),
422
- }, { parent, dependsOn: cfnStackDeps, provider });
423
- const autoScalingGroupName = cfnStack.outputs.apply((outputs) => {
424
- if (!("NodeGroup" in outputs)) {
425
- throw new pulumi.ResourceError("CloudFormation stack is not ready. Stack output key 'NodeGroup' does not exist.", parent);
426
- }
427
- return outputs["NodeGroup"];
428
- });
429
- return {
430
- nodeSecurityGroup: nodeSecurityGroup,
431
- cfnStack: cfnStack,
432
- autoScalingGroupName: autoScalingGroupName,
433
- extraNodeSecurityGroups: args.extraNodeSecurityGroups,
434
- };
435
- }
436
- /**
437
- * Create a self-managed node group using a Launch Template and an ASG.
438
- *
439
- * See for more details:
440
- * https://docs.aws.amazon.com/eks/latest/userguide/worker.html
441
- */
442
- function createNodeGroupV2(name, args, parent, provider) {
443
- const core = args.cluster instanceof cluster_1.Cluster ? args.cluster.core : args.cluster;
444
- return createNodeGroupV2Internal(name, args, pulumi.output(core), parent, provider);
445
- }
446
- exports.createNodeGroupV2 = createNodeGroupV2;
447
- function createNodeGroupV2Internal(name, args, core, parent, provider) {
448
- var _a, _b, _c, _d, _e, _f, _g, _h;
449
- const instanceProfileArn = core.apply((c) => {
450
- var _a, _b;
451
- if (!args.instanceProfile && !c.nodeGroupOptions.instanceProfile) {
452
- throw new pulumi.ResourceError(`an instanceProfile is required`, parent);
453
- }
454
- return (_b = (_a = args.instanceProfile) === null || _a === void 0 ? void 0 : _a.arn) !== null && _b !== void 0 ? _b : c.nodeGroupOptions.instanceProfile.arn;
455
- });
456
- core.apply((c) => {
457
- if (c.nodeGroupOptions.nodeSecurityGroup && args.nodeSecurityGroup) {
458
- if (c.nodeSecurityGroupTags &&
459
- c.nodeGroupOptions.nodeSecurityGroup.id !== args.nodeSecurityGroup.id) {
460
- throw new pulumi.ResourceError(`The NodeGroup's nodeSecurityGroup and the cluster option nodeSecurityGroupTags are mutually exclusive. Choose a single approach`, parent);
461
- }
462
- }
463
- });
464
- if (args.nodePublicKey && args.keyName) {
465
- throw new pulumi.ResourceError("nodePublicKey and keyName are mutually exclusive. Choose a single approach", parent);
466
- }
467
- if (args.amiId && args.gpu) {
468
- throw new pulumi.ResourceError("amiId and gpu are mutually exclusive.", parent);
469
- }
470
- if (args.nodeUserDataOverride &&
471
- (args.nodeUserData ||
472
- args.labels ||
473
- args.taints ||
474
- args.kubeletExtraArgs ||
475
- args.bootstrapExtraArgs)) {
476
- throw new pulumi.ResourceError("nodeUserDataOverride and any combination of {nodeUserData, labels, taints, kubeletExtraArgs, or bootstrapExtraArgs} is mutually exclusive.", parent);
477
- }
478
- let nodeSecurityGroup;
479
- const eksCluster = core.cluster;
480
- const nodeGroupDeps = core.apply((c) => {
481
- const result = [];
482
- if (c.vpcCni !== undefined) {
483
- result.push(c.vpcCni);
484
- }
485
- if (c.eksNodeAccess !== undefined) {
486
- result.push(c.eksNodeAccess);
487
- }
488
- return result;
489
- });
490
- let eksClusterIngressRule = args.clusterIngressRule;
491
- if (args.nodeSecurityGroup) {
492
- nodeSecurityGroup = args.nodeSecurityGroup;
493
- if (eksClusterIngressRule === undefined) {
494
- throw new pulumi.ResourceError(`invalid args for node group ${name}, clusterIngressRule is required when nodeSecurityGroup is manually specified`, parent);
495
- }
496
- }
497
- else {
498
- [nodeSecurityGroup, eksClusterIngressRule] = (0, securitygroup_1.createNodeGroupSecurityGroup)(name, {
499
- vpcId: core.vpcId,
500
- clusterSecurityGroup: core.clusterSecurityGroup,
501
- eksCluster: eksCluster,
502
- tags: core.apply((c) => (Object.assign(Object.assign({}, c.nodeSecurityGroupTags), c.tags))),
503
- }, parent);
504
- }
505
- // This apply is necessary in s.t. the launchConfiguration picks up a
506
- // dependency on the eksClusterIngressRule. The nodes may fail to
507
- // connect to the cluster if we attempt to create them before the
508
- // ingress rule is applied.
509
- const nodeSecurityGroupId = pulumi
510
- .all([nodeSecurityGroup.id, eksClusterIngressRule.id])
511
- .apply(([id]) => id);
512
- // Collect the IDs of any extra, user-specific security groups.
513
- const extraNodeSecurityGroupIds = pulumi.all([args.extraNodeSecurityGroups]).apply(([sg]) => {
514
- if (sg === undefined) {
515
- return [];
516
- }
517
- // Map out the ARNs of all of the instanceRoles.
518
- return sg.map((sg) => sg.id);
519
- });
520
- // If requested, add a new EC2 KeyPair for SSH access to the instances.
521
- let keyName = args.keyName;
522
- if (args.nodePublicKey) {
523
- const key = new aws.ec2.KeyPair(`${name}-keyPair`, {
524
- publicKey: args.nodePublicKey,
525
- }, { parent, provider });
526
- keyName = key.keyName;
527
- }
528
- const awsRegion = pulumi.output(aws.getRegion({}, { parent, async: true }));
529
- const userDataArg = args.nodeUserData || pulumi.output("");
530
- const kubeletExtraArgs = args.kubeletExtraArgs ? args.kubeletExtraArgs.split(" ") : [];
531
- if (args.labels) {
532
- const parts = [];
533
- for (const key of Object.keys(args.labels)) {
534
- parts.push(key + "=" + args.labels[key]);
535
- }
536
- if (parts.length > 0) {
537
- kubeletExtraArgs.push("--node-labels=" + parts.join(","));
538
- }
539
- }
540
- if (args.taints) {
541
- const parts = [];
542
- for (const key of Object.keys(args.taints)) {
543
- const taint = args.taints[key];
544
- parts.push(key + "=" + taint.value + ":" + taint.effect);
545
- }
546
- if (parts.length > 0) {
547
- kubeletExtraArgs.push("--register-with-taints=" + parts.join(","));
548
- }
549
- }
550
- let bootstrapExtraArgs = args.bootstrapExtraArgs ? " " + args.bootstrapExtraArgs : "";
551
- if (kubeletExtraArgs.length === 1) {
552
- // For backward compatibility with previous versions of this package, don't wrap a single argument with `''`.
553
- bootstrapExtraArgs += ` --kubelet-extra-args ${kubeletExtraArgs[0]}`;
554
- }
555
- else if (kubeletExtraArgs.length > 1) {
556
- bootstrapExtraArgs += ` --kubelet-extra-args '${kubeletExtraArgs.join(" ")}'`;
557
- }
558
- const userdata = pulumi
559
- .all([
560
- awsRegion,
561
- eksCluster.name,
562
- eksCluster.endpoint,
563
- eksCluster.certificateAuthority,
564
- name,
565
- userDataArg,
566
- args.nodeUserDataOverride,
567
- ])
568
- .apply(([region, clusterName, clusterEndpoint, clusterCa, stackName, customUserData, nodeUserDataOverride,]) => {
569
- if (nodeUserDataOverride !== undefined && nodeUserDataOverride !== "") {
570
- return nodeUserDataOverride;
571
- }
572
- if (customUserData !== "") {
573
- customUserData = `cat >/opt/user-data <<${stackName}-user-data
574
- ${customUserData}
575
- ${stackName}-user-data
576
- chmod +x /opt/user-data
577
- /opt/user-data
578
- `;
579
- }
580
- return `#!/bin/bash
581
-
582
- /etc/eks/bootstrap.sh --apiserver-endpoint "${clusterEndpoint}" --b64-cluster-ca "${clusterCa.data}" "${clusterName}"${bootstrapExtraArgs}
583
- ${customUserData}
584
- `;
585
- })
586
- .apply((x) => Buffer.from(x, "utf-8").toString("base64")); // Launch Templates require user data to be passed as base64.
587
- const version = pulumi.output(args.version || core.cluster.version);
588
- const amiId = args.amiId || getRecommendedAMI(args, version, parent);
589
- // Enable auto-assignment of public IP addresses on worker nodes for
590
- // backwards compatibility on existing EKS clusters launched with it
591
- // enabled. Defaults to `true`.
592
- let nodeAssociatePublicIpAddress = true;
593
- if (args.nodeAssociatePublicIpAddress !== undefined) {
594
- nodeAssociatePublicIpAddress = args.nodeAssociatePublicIpAddress;
595
- }
596
- const numeric = new RegExp("^\\d+$");
597
- // We need to wrap the validation in a pulumi.all as MLCs could supply pulumi.Output<T> or T.
598
- pulumi
599
- .all([args.nodeRootVolumeIops, args.nodeRootVolumeType, args.nodeRootVolumeThroughput])
600
- .apply(([nodeRootVolumeIops, nodeRootVolumeType, nodeRootVolumeThroughput]) => {
601
- if (nodeRootVolumeIops && nodeRootVolumeType !== "io1") {
602
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of non-io1 type with provisioned IOPS (nodeRootVolumeIops).", parent);
603
- }
604
- if (nodeRootVolumeType === "io1" && nodeRootVolumeIops) {
605
- if (!numeric.test(nodeRootVolumeIops === null || nodeRootVolumeIops === void 0 ? void 0 : nodeRootVolumeIops.toString())) {
606
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of io1 type without provisioned IOPS (nodeRootVolumeIops) as integer value.", parent);
607
- }
608
- }
609
- if (nodeRootVolumeThroughput && nodeRootVolumeType !== "gp3") {
610
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of non-gp3 type with provisioned throughput (nodeRootVolumeThroughput).", parent);
611
- }
612
- if (nodeRootVolumeType === "gp3" && nodeRootVolumeThroughput) {
613
- if (!numeric.test(nodeRootVolumeThroughput === null || nodeRootVolumeThroughput === void 0 ? void 0 : nodeRootVolumeThroughput.toString())) {
614
- throw new pulumi.ResourceError("Cannot create a cluster node root volume of gp3 type without provisioned throughput (nodeRootVolumeThroughput) as integer value.", parent);
615
- }
616
- }
617
- });
618
- const marketOptions = args.spotPrice
619
- ? {
620
- marketType: "spot",
621
- spotOptions: {
622
- maxPrice: args.spotPrice,
623
- },
624
- }
625
- : undefined;
626
- const device = pulumi.output(amiId).apply((id) => aws.ec2.getAmi({
627
- owners: ["self", "amazon"],
628
- filters: [
629
- {
630
- name: "image-id",
631
- values: [id],
632
- },
633
- ],
634
- }, { parent })).blockDeviceMappings[0].deviceName;
635
- const nodeLaunchTemplate = new aws.ec2.LaunchTemplate(`${name}-launchTemplate`, {
636
- imageId: amiId,
637
- instanceType: args.instanceType || "t2.medium",
638
- iamInstanceProfile: { arn: instanceProfileArn },
639
- keyName: keyName,
640
- instanceMarketOptions: marketOptions,
641
- blockDeviceMappings: [
642
- {
643
- deviceName: device,
644
- ebs: {
645
- encrypted: ((_a = args.nodeRootVolumeEncrypted) !== null && _a !== void 0 ? _a : false) ? "true" : "false",
646
- volumeSize: (_b = args.nodeRootVolumeSize) !== null && _b !== void 0 ? _b : 20,
647
- volumeType: (_c = args.nodeRootVolumeType) !== null && _c !== void 0 ? _c : "gp2",
648
- iops: args.nodeRootVolumeIops,
649
- throughput: args.nodeRootVolumeThroughput,
650
- deleteOnTermination: ((_d = args.nodeRootVolumeDeleteOnTermination) !== null && _d !== void 0 ? _d : true) ? "true" : "false",
651
- },
652
- },
653
- ],
654
- networkInterfaces: [
655
- {
656
- associatePublicIpAddress: String(nodeAssociatePublicIpAddress),
657
- securityGroups: pulumi
658
- .all([nodeSecurityGroupId, extraNodeSecurityGroupIds])
659
- .apply(([sg, extraSG]) => [sg, ...extraSG]),
660
- },
661
- ],
662
- metadataOptions: args.metadataOptions,
663
- userData: userdata,
664
- tagSpecifications: args.launchTemplateTagSpecifications,
665
- monitoring: args.enableDetailedMonitoring == null
666
- ? undefined
667
- : {
668
- enabled: args.enableDetailedMonitoring,
669
- },
670
- }, { parent, provider });
671
- // Compute the worker node group subnets to use from the various approaches.
672
- let workerSubnetIds;
673
- if (args.nodeSubnetIds !== undefined) {
674
- // Use the specified override subnetIds.
675
- workerSubnetIds = pulumi.output(args.nodeSubnetIds);
676
- }
677
- else {
678
- workerSubnetIds = core.apply((c) => {
679
- if (c.privateSubnetIds !== undefined) {
680
- // Use the specified private subnetIds.
681
- return Promise.resolve(c.privateSubnetIds);
682
- }
683
- else if (c.publicSubnetIds !== undefined) {
684
- // Use the specified public subnetIds.
685
- return Promise.resolve(c.publicSubnetIds);
686
- }
687
- else {
688
- // Use subnetIds from the cluster. Compute / auto-discover the private worker subnetIds from this set.
689
- return computeWorkerSubnets(parent, c.subnetIds);
690
- }
691
- });
692
- }
693
- const asgTags = pulumi
694
- .all([eksCluster.name, args.autoScalingGroupTags])
695
- .apply(([clusterName, tags]) => inputTagsToASGTags(clusterName, tags));
696
- const launchTemplateVersion = nodeLaunchTemplate.latestVersion.apply((v) => v.toString());
697
- const asGroup = new aws.autoscaling.Group(name, {
698
- minSize: (_e = args === null || args === void 0 ? void 0 : args.minSize) !== null && _e !== void 0 ? _e : 1,
699
- maxSize: (_f = args === null || args === void 0 ? void 0 : args.maxSize) !== null && _f !== void 0 ? _f : 2,
700
- desiredCapacity: (_g = args === null || args === void 0 ? void 0 : args.desiredCapacity) !== null && _g !== void 0 ? _g : 2,
701
- launchTemplate: {
702
- name: nodeLaunchTemplate.name,
703
- version: launchTemplateVersion,
704
- },
705
- vpcZoneIdentifiers: workerSubnetIds,
706
- instanceRefresh: {
707
- strategy: "Rolling",
708
- preferences: {
709
- minHealthyPercentage: (_h = args.minRefreshPercentage) !== null && _h !== void 0 ? _h : 50,
710
- },
711
- },
712
- tags: asgTags,
713
- defaultInstanceWarmup: args.defaultInstanceWarmup,
714
- }, { parent, dependsOn: nodeGroupDeps, provider });
715
- return {
716
- nodeSecurityGroup: nodeSecurityGroup,
717
- autoScalingGroup: asGroup,
718
- extraNodeSecurityGroups: args.extraNodeSecurityGroups,
719
- };
720
- }
721
- function inputTagsToASGTags(clusterName, tags) {
722
- const asgTags = Object.entries(tags !== null && tags !== void 0 ? tags : {}).map(([key, value]) => ({
723
- key,
724
- value,
725
- propagateAtLaunch: true,
726
- }));
727
- asgTags.push({
728
- value: "owned",
729
- key: "kubernetes.io/cluster/" + clusterName,
730
- propagateAtLaunch: true,
731
- }, {
732
- key: "Name",
733
- value: clusterName + "-worker",
734
- propagateAtLaunch: true,
735
- });
736
- return asgTags;
737
- }
738
- /** computeWorkerSubnets attempts to determine the subset of the given subnets to use for worker nodes.
739
- *
740
- * As per https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html, an EKS cluster that is attached to public
741
- * and private subnets will only expose its API service to workers on the private subnets. Any workers attached to the
742
- * public subnets will be unable to communicate with the API server.
743
- *
744
- * If all of the given subnet IDs are public, the list of subnet IDs is returned as-is. If any private subnet is given,
745
- * only the IDs of the private subnets are returned. A subnet is deemed private iff it has no route in its route table
746
- * that routes directly to an internet gateway. If any such route exists in a subnet's route table, it is treated as
747
- * public.
748
- */
749
- function computeWorkerSubnets(parent, subnetIds) {
750
- return __awaiter(this, void 0, void 0, function* () {
751
- const publicSubnets = [];
752
- const privateSubnets = [];
753
- for (const subnetId of subnetIds) {
754
- // Fetch the route table for this subnet.
755
- const routeTable = yield getRouteTableAsync(parent, subnetId);
756
- // Once we have the route table, check its list of routes for a route to an internet gateway.
757
- const hasInternetGatewayRoute = routeTable.routes.find((r) => !!r.gatewayId && !isPrivateCIDRBlock(r.cidrBlock)) !==
758
- undefined;
759
- if (hasInternetGatewayRoute) {
760
- publicSubnets.push(subnetId);
761
- }
762
- else {
763
- privateSubnets.push(subnetId);
764
- }
765
- }
766
- return privateSubnets.length === 0 ? publicSubnets : privateSubnets;
767
- });
768
- }
769
- exports.computeWorkerSubnets = computeWorkerSubnets;
770
- function getRouteTableAsync(parent, subnetId) {
771
- return __awaiter(this, void 0, void 0, function* () {
772
- const invokeOpts = { parent, async: true };
773
- try {
774
- // Attempt to get the explicit route table for this subnet. If there is no explicit rouute table for
775
- // this subnet, this call will throw.
776
- return yield aws.ec2.getRouteTable({ subnetId }, invokeOpts);
777
- }
778
- catch (_a) {
779
- // If we reach this point, the subnet may not have an explicitly associated route table. In this case
780
- // the subnet is associated with its VPC's main route table (see
781
- // https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html#RouteTables for details).
782
- const subnet = yield aws.ec2.getSubnet({ id: subnetId }, invokeOpts);
783
- const mainRouteTableInfo = yield aws.ec2.getRouteTables({
784
- vpcId: subnet.vpcId,
785
- filters: [
786
- {
787
- name: "association.main",
788
- values: ["true"],
789
- },
790
- ],
791
- }, invokeOpts);
792
- return yield aws.ec2.getRouteTable({ routeTableId: mainRouteTableInfo.ids[0] }, invokeOpts);
793
- }
794
- });
795
- }
796
- /**
797
- * Returns true if the given CIDR block falls within a private range [1].
798
- * [1] https://en.wikipedia.org/wiki/Private_network
799
- */
800
- function isPrivateCIDRBlock(cidrBlock) {
801
- const privateA = new netmask.Netmask("10.0.0.0/8");
802
- const privateB = new netmask.Netmask("172.16.0.0/12");
803
- const privateC = new netmask.Netmask("192.168.0.0/16");
804
- return (privateA.contains(cidrBlock) || privateB.contains(cidrBlock) || privateC.contains(cidrBlock));
805
- }
806
- /**
807
- * Iterates through the tags map creating AWS ASG-style tags
808
- */
809
- function tagsToAsgTags(tagsInput) {
810
- return pulumi.output(tagsInput).apply((tags) => {
811
- let output = "";
812
- for (const tag of Object.keys(tags)) {
813
- output += `
814
- - Key: ${tag}
815
- Value: ${tags[tag]}
816
- PropagateAtLaunch: 'true'`;
817
- }
818
- return output;
819
- });
820
- }
821
- /**
822
- * ManagedNodeGroup is a component that wraps creating an AWS managed node group.
823
- *
824
- * See for more details:
825
- * https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html
826
- */
827
- class ManagedNodeGroup extends pulumi.ComponentResource {
828
- /**
829
- * Create a new AWS managed node group.
830
- *
831
- * @param name The _unique_ name of this component.
832
- * @param args The arguments for this node group.
833
- * @param opts A bag of options that control this component's behavior.
834
- */
835
- constructor(name, args, opts) {
836
- super("eks:index:ManagedNodeGroup", name, args, opts);
837
- this.nodeGroup = createManagedNodeGroup(name, args, this, opts === null || opts === void 0 ? void 0 : opts.provider);
838
- this.registerOutputs(undefined);
839
- }
840
- }
841
- exports.ManagedNodeGroup = ManagedNodeGroup;
842
- /**
843
- * This is a variant of `ManagedNodeGroup` that is used for the MLC `ManagedNodeGroup`. We don't just use
844
- * `ManagedNodeGroup`, because we need to accept `ClusterInternal` as the `cluster` arg, so we can correctly
845
- * pull out `cluster.core` for use in creating the `NodeGroupV2`.
846
- *
847
- * @internal
848
- */
849
- class ManagedNodeGroupInternal extends pulumi.ComponentResource {
850
- constructor(name, args, opts) {
851
- const type = "eks:index:ManagedNodeGroup";
852
- if (opts === null || opts === void 0 ? void 0 : opts.urn) {
853
- const props = {
854
- nodeGroup: undefined,
855
- };
856
- super(type, name, props, opts);
857
- return;
858
- }
859
- super(type, name, args, opts);
860
- const core = pulumi
861
- .output(args.cluster)
862
- .apply((c) => (c instanceof cluster_1.ClusterInternal ? c.core : c));
863
- const group = createManagedNodeGroupInternal(name, args, core, this, opts === null || opts === void 0 ? void 0 : opts.provider);
864
- this.nodeGroup = pulumi.output(group);
865
- this.registerOutputs({
866
- nodeGroup: this.nodeGroup,
867
- });
868
- }
869
- }
870
- exports.ManagedNodeGroupInternal = ManagedNodeGroupInternal;
871
- /**
872
- * Create an AWS managed node group.
873
- *
874
- * See for more details:
875
- * https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html
876
- */
877
- function createManagedNodeGroup(name, args, parent, provider) {
878
- const core = args.cluster instanceof cluster_1.Cluster ? args.cluster.core : args.cluster;
879
- return createManagedNodeGroupInternal(name, args, pulumi.output(core), parent !== null && parent !== void 0 ? parent : core.cluster, provider);
880
- }
881
- exports.createManagedNodeGroup = createManagedNodeGroup;
882
- function createManagedNodeGroupInternal(name, args, core, parent, provider) {
883
- // Compute the nodegroup role.
884
- if (!args.nodeRole && !args.nodeRoleArn) {
885
- // throw new pulumi.ResourceError(`An IAM role, or role ARN must be provided to create a managed node group`);
886
- throw new pulumi.ResourceError(`An IAM role, or role ARN must be provided to create a managed node group`, parent);
887
- }
888
- if (args.nodeRole && args.nodeRoleArn) {
889
- throw new pulumi.ResourceError("nodeRole and nodeRoleArn are mutually exclusive to create a managed node group", parent);
890
- }
891
- let roleArn;
892
- if (args.nodeRoleArn) {
893
- roleArn = args.nodeRoleArn;
894
- }
895
- else if (args.nodeRole) {
896
- roleArn = pulumi.output(args.nodeRole).apply((r) => r.arn);
897
- }
898
- else {
899
- throw new pulumi.ResourceError("The managed node group role provided is undefined", parent);
900
- }
901
- // Check that the nodegroup role has been set on the cluster to
902
- // ensure that the aws-auth configmap was properly formed.
903
- const nodegroupRole = pulumi.all([core.instanceRoles, roleArn]).apply(([roles, rArn]) => {
904
- // Map out the ARNs of all of the instanceRoles. Note that the roles array may be undefined if
905
- // unspecified by the Pulumi program.
906
- const roleArns = roles ? roles.map((role) => role.arn) : [];
907
- // Try finding the nodeRole in the ARNs array.
908
- return pulumi.all([roleArns, rArn]).apply(([arns, arn]) => {
909
- return arns.find((a) => a === arn);
910
- });
911
- });
912
- pulumi
913
- .all([core.cluster.accessConfig.authenticationMode, nodegroupRole])
914
- .apply(([authMode, role]) => {
915
- // access entries can be added out of band, so we don't require them to be set in the cluster.
916
- if (!(0, authenticationMode_1.supportsAccessEntries)(authMode) && !role) {
917
- throw new pulumi.ResourceError(`A managed node group cannot be created without first setting its role in the cluster's instanceRoles`, parent);
918
- }
919
- });
920
- // Compute the node group subnets to use.
921
- let subnetIds;
922
- if (args.subnetIds !== undefined) {
923
- subnetIds = pulumi.output(args.subnetIds);
924
- }
925
- else {
926
- subnetIds = core.apply((c) => {
927
- if (c.subnetIds !== undefined) {
928
- return c.subnetIds;
929
- }
930
- else if (c.privateSubnetIds !== undefined) {
931
- return c.privateSubnetIds;
932
- }
933
- else if (c.publicSubnetIds !== undefined) {
934
- return c.publicSubnetIds;
935
- }
936
- else {
937
- return [];
938
- }
939
- });
940
- }
941
- // Omit the cluster from the args.
942
- const nodeGroupArgs = Object.assign({}, args);
943
- if ("cluster" in nodeGroupArgs) {
944
- delete nodeGroupArgs.cluster;
945
- }
946
- // Create a custom launch template for the managed node group if the user specifies either kubeletExtraArgs or bootstrapExtraArgs.
947
- // If the user sepcifies a custom LaunchTemplate, we throw an error and suggest that the user include this in the launch template that they are providing.
948
- // If neither of these are provided, we can use the default launch template for managed node groups.
949
- if (args.launchTemplate &&
950
- (args.kubeletExtraArgs || args.bootstrapExtraArgs || args.enableIMDSv2)) {
951
- throw new pulumi.ResourceError("If you provide a custom launch template, you cannot provide kubeletExtraArgs, bootstrapExtraArgs or enableIMDSv2. Please include these in the launch template that you are providing.", parent);
952
- }
953
- let launchTemplate;
954
- if (args.kubeletExtraArgs || args.bootstrapExtraArgs || args.enableIMDSv2) {
955
- launchTemplate = createMNGCustomLaunchTemplate(name, args, core, parent, provider);
956
- // Disk size is specified in the launch template.
957
- delete nodeGroupArgs.diskSize;
958
- }
959
- if (launchTemplate === null || launchTemplate === void 0 ? void 0 : launchTemplate.imageId) {
960
- // EKS doesn't allow setting the kubernetes version in the node group if an image id is provided within the launch template.
961
- delete nodeGroupArgs.version;
962
- }
963
- // Make the aws-auth configmap a dependency of the node group.
964
- const ngDeps = core.apply((c) => (c.eksNodeAccess !== undefined ? [c.eksNodeAccess] : []));
965
- // Create the managed node group.
966
- const nodeGroup = new aws.eks.NodeGroup(name, Object.assign(Object.assign({}, nodeGroupArgs), { clusterName: args.clusterName || core.cluster.name, nodeRoleArn: roleArn, scalingConfig: pulumi.all([args.scalingConfig]).apply(([config]) => {
967
- var _a, _b, _c;
968
- const desiredSize = (_a = config === null || config === void 0 ? void 0 : config.desiredSize) !== null && _a !== void 0 ? _a : 2;
969
- const minSize = (_b = config === null || config === void 0 ? void 0 : config.minSize) !== null && _b !== void 0 ? _b : 1;
970
- const maxSize = (_c = config === null || config === void 0 ? void 0 : config.maxSize) !== null && _c !== void 0 ? _c : 2;
971
- return {
972
- desiredSize: desiredSize,
973
- minSize: minSize,
974
- maxSize: maxSize,
975
- };
976
- }), subnetIds: subnetIds, launchTemplate: launchTemplate
977
- ? {
978
- id: launchTemplate.id,
979
- version: launchTemplate.latestVersion.apply((version) => {
980
- return `${version}`;
981
- }),
982
- }
983
- : args.launchTemplate }), { parent: parent, dependsOn: ngDeps, provider });
984
- return nodeGroup;
985
- }
986
- /**
987
- * Create a custom launch template for the managed node group if the user specifies either kubeletExtraArgs or bootstrapExtraArgs.
988
- */
989
- function createMNGCustomLaunchTemplate(name, args, core, parent, provider) {
990
- let userData;
991
- // If the user specifies either kubeletExtraArgs or bootstrapExtraArgs, we need to create a base64 encoded user data script.
992
- if (args.kubeletExtraArgs || args.bootstrapExtraArgs) {
993
- const kubeletExtraArgs = args.kubeletExtraArgs ? args.kubeletExtraArgs.split(" ") : [];
994
- let bootstrapExtraArgs = args.bootstrapExtraArgs ? " " + args.bootstrapExtraArgs : "";
995
- if (kubeletExtraArgs.length === 1) {
996
- // For backward compatibility with previous versions of this package, don't wrap a single argument with `''`.
997
- bootstrapExtraArgs += ` --kubelet-extra-args ${kubeletExtraArgs[0]}`;
998
- }
999
- else if (kubeletExtraArgs.length > 1) {
1000
- bootstrapExtraArgs += ` --kubelet-extra-args '${kubeletExtraArgs.join(" ")}'`;
1001
- }
1002
- const userdata = pulumi
1003
- .all([
1004
- core.cluster.name,
1005
- core.cluster.endpoint,
1006
- core.cluster.certificateAuthority.data,
1007
- args.clusterName,
1008
- ])
1009
- .apply(([clusterName, clusterEndpoint, clusterCertAuthority, argsClusterName]) => {
1010
- return `MIME-Version: 1.0
1011
- Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="
1012
-
1013
- --==MYBOUNDARY==
1014
- Content-Type: text/x-shellscript; charset="us-ascii"
1015
-
1016
- #!/bin/bash
1017
-
1018
- /etc/eks/bootstrap.sh --apiserver-endpoint "${clusterEndpoint}" --b64-cluster-ca "${clusterCertAuthority}" "${argsClusterName || clusterName}"${bootstrapExtraArgs}
1019
- --==MYBOUNDARY==--`;
1020
- });
1021
- // Encode the user data as base64.
1022
- userData = pulumi
1023
- .output(userdata)
1024
- .apply((ud) => Buffer.from(ud, "utf-8").toString("base64"));
1025
- }
1026
- // If the user specifies enableIMDSv2, we need to set the metadata options in the launch template.
1027
- const metadataOptions = args.enableIMDSv2
1028
- ? { httpTokens: "required", httpPutResponseHopLimit: 2, httpEndpoint: "enabled" }
1029
- : undefined;
1030
- const blockDeviceMappings = args.diskSize
1031
- ? [
1032
- {
1033
- // /dev/xvda is the default device name for the root volume on an Amazon Linux 2 & AL2023 instance.
1034
- deviceName: "/dev/xvda",
1035
- ebs: {
1036
- volumeSize: args.diskSize,
1037
- },
1038
- },
1039
- ]
1040
- : undefined;
1041
- return new aws.ec2.LaunchTemplate(`${name}-launchTemplate`, {
1042
- blockDeviceMappings,
1043
- userData,
1044
- metadataOptions,
1045
- // We need to supply an imageId if userData is set, otherwise AWS will attempt to merge the user data which will result in
1046
- // nodes failing to join the cluster.
1047
- imageId: userData ? getRecommendedAMI(args, core.cluster.version, parent) : undefined,
1048
- }, { parent, provider });
1049
- }
1050
- /**
1051
- * getRecommendedAMI returns the recommended AMI to use for a given configuration
1052
- * when none is provided by the user.
1053
- *
1054
- * See: https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html
1055
- */
1056
- function getRecommendedAMI(args, k8sVersion, parent) {
1057
- const gpu = "gpu" in args ? args.gpu : undefined;
1058
- let instanceTypes;
1059
- if ("instanceType" in args && args.instanceType) {
1060
- instanceTypes = [args.instanceType];
1061
- }
1062
- else if ("instanceTypes" in args) {
1063
- instanceTypes = args.instanceTypes;
1064
- }
1065
- const amiType = getAMIType(args.amiType, gpu, instanceTypes);
1066
- // if specified use the version from the args, otherwise use the version from the cluster.
1067
- const version = args.version ? args.version : k8sVersion;
1068
- const amiID = pulumi.output([version, amiType]).apply(([version, type]) => {
1069
- const parameterName = `/aws/service/eks/optimized-ami/${version}/${type}/recommended/image_id`;
1070
- return pulumi.output(aws.ssm.getParameter({ name: parameterName }, { parent, async: true }))
1071
- .value;
1072
- });
1073
- return amiID;
1074
- }
1075
- /**
1076
- * ec2InstanceRegex is a regular expression that can be used to parse an EC2
1077
- * instance type string into its component parts. The component parts are:
1078
- * - family: The instance family (e.g., "c5")
1079
- * - generation: The instance generation (e.g., "2")
1080
- * - processor: The processor type (e.g., "g")
1081
- * - additionalCapabilities: Additional capabilities (e.g., "n")
1082
- * - size: The instance size (e.g., "large")
1083
- * These parts result in a string of the form: `c52gn.large`
1084
- */
1085
- const ec2InstanceRegex = /([a-z]+)([0-9]+)([a-z])?\-?([a-z]+)?\.([a-zA-Z0-9\-]+)/;
1086
- /**
1087
- * isGravitonInstance returns true if the instance type is a Graviton instance.
1088
- * We determine this by checking if the third character of the instance type is
1089
- * a "g".
1090
- *
1091
- * See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html.
1092
- */
1093
- function isGravitonInstance(instanceType) {
1094
- const match = instanceType.toString().match(ec2InstanceRegex);
1095
- if (!match) {
1096
- throw new pulumi.ResourceError(`Invalid EC2 instance type: ${instanceType}`, undefined);
1097
- }
1098
- const processorFamily = match[3];
1099
- return processorFamily === "g";
1100
- }
1101
- exports.isGravitonInstance = isGravitonInstance;
1102
- /**
1103
- * getAMIType returns the AMI type to use for the given configuration based on the
1104
- * architecture of the instance type.
1105
- */
1106
- function getAMIType(amiType, gpu, instanceTypes) {
1107
- const architecture = pulumi.output(instanceTypes).apply((instanceTypes) => {
1108
- return pulumi
1109
- .all(instanceTypes !== null && instanceTypes !== void 0 ? instanceTypes : [])
1110
- .apply((instanceTypes) => getArchitecture(instanceTypes));
1111
- });
1112
- return pulumi.all([amiType, gpu, architecture]).apply(([amiType, gpu, architecture]) => {
1113
- if (amiType) {
1114
- // Return the user-specified AMI type.
1115
- return amiType;
1116
- }
1117
- if (gpu) {
1118
- // Return the Amazon Linux 2 GPU AMI type.
1119
- return "amazon-linux-2-gpu";
1120
- }
1121
- if (architecture === "arm64") {
1122
- // Return the Amazon Linux 2 ARM64 AMI type.
1123
- return "amazon-linux-2-arm64";
1124
- }
1125
- return "amazon-linux-2";
1126
- });
1127
- }
1128
- /**
1129
- * Determines the architecture based on the provided instance types. Defaults to "x86_64" if no instance types are provided.
1130
- *
1131
- * @param instanceTypes - An array of instance types.
1132
- * @returns The architecture of the instance types, either "arm64" or "x86_64".
1133
- * @throws {pulumi.ResourceError} If the provided instance types do not share a common architecture.
1134
- */
1135
- function getArchitecture(instanceTypes) {
1136
- let hasGravitonInstances = false;
1137
- let hasX64Instances = false;
1138
- instanceTypes.forEach((instanceType) => {
1139
- if (isGravitonInstance(instanceType)) {
1140
- hasGravitonInstances = true;
1141
- }
1142
- else {
1143
- hasX64Instances = true;
1144
- }
1145
- if (hasGravitonInstances && hasX64Instances) {
1146
- throw new pulumi.ResourceError("Cannot determine architecture of instance types. The provided instance types do not share a common architecture", undefined);
1147
- }
1148
- });
1149
- return hasGravitonInstances ? "arm64" : "x86_64";
1150
- }
1151
- exports.getArchitecture = getArchitecture;
1152
- //# sourceMappingURL=nodegroup.js.map