diff --git a/API.md b/API.md index 943ee2e..c479dcd 100644 --- a/API.md +++ b/API.md @@ -162,6 +162,7 @@ Any object. | helmExtraValues | any | *No description.* | | namespace | string | *No description.* | | nodeRole | aws-cdk-lib.aws_iam.Role | *No description.* | +| serviceAccountName | string | *No description.* | | version | string | *No description.* | --- @@ -218,6 +219,16 @@ public readonly nodeRole: Role; --- +##### `serviceAccountName`Required + +```typescript +public readonly serviceAccountName: string; +``` + +- *Type:* string + +--- + ##### `version`Optional ```typescript @@ -249,6 +260,7 @@ const karpenterProps: KarpenterProps = { ... } | helmExtraValues | any | Extra values to pass to the Karpenter Helm chart. | | namespace | string | The Kubernetes namespace to install to. | | nodeRole | aws-cdk-lib.aws_iam.Role | Custom NodeRole to pass for Karpenter Nodes. | +| serviceAccountName | string | The Kubernetes ServiceAccount name to use. | | version | string | The helm chart version to install. | --- @@ -302,6 +314,19 @@ Custom NodeRole to pass for Karpenter Nodes. --- +##### `serviceAccountName`Optional + +```typescript +public readonly serviceAccountName: string; +``` + +- *Type:* string +- *Default:* karpenter + +The Kubernetes ServiceAccount name to use. + +--- + ##### `version`Optional ```typescript diff --git a/src/index.ts b/src/index.ts index d8c5345..bafb201 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,13 @@ export interface KarpenterProps { */ readonly namespace?: string; + /** + * The Kubernetes ServiceAccount name to use + * + * @default karpenter + */ + readonly serviceAccountName?: string; + /** * The helm chart version to install * @@ -41,6 +48,7 @@ export interface KarpenterProps { export class Karpenter extends Construct { public readonly cluster: Cluster; public readonly namespace: string; + public readonly serviceAccountName: string; public readonly version?: string; public readonly nodeRole: Role; public readonly helmExtraValues: any; @@ -52,6 +60,7 @@ export class Karpenter extends Construct { this.cluster = props.cluster; this.namespace = props.namespace ?? 'karpenter'; + this.serviceAccountName = props.serviceAccountName ?? 'karpenter'; this.version = props.version; this.helmExtraValues = props.helmExtraValues ?? {}; @@ -61,9 +70,8 @@ export class Karpenter extends Construct { * * We will also create a role mapping in the `aws-auth` ConfigMap so that the nodes can authenticate * with the Kubernetes API using IAM. - */ - - /* Create Node Role if nodeRole not added as prop + * + * Create Node Role if nodeRole not added as prop * Make sure that the Role that is added does not have an Instance Profile associated to it * since we will create it here. */ @@ -110,6 +118,7 @@ export class Karpenter extends Construct { }); this.serviceAccount = this.cluster.addServiceAccount('karpenter', { + name: this.serviceAccountName, namespace: this.namespace, }); this.serviceAccount.node.addDependency(namespace); diff --git a/test/index.test.ts b/test/index.test.ts index 18cf89a..ef2f4e6 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -10,7 +10,7 @@ describe('Karpenter installation', () => { const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_21, + version: KubernetesVersion.V1_27, }); new Karpenter(stack, 'Karpenter', { @@ -28,7 +28,7 @@ describe('Karpenter installation', () => { const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_21, + version: KubernetesVersion.V1_27, }); // Create Karpenter install with non-default version @@ -49,7 +49,7 @@ describe('Karpenter installation', () => { const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_21, + version: KubernetesVersion.V1_27, }); // Create Karpenter install with non-default namespace @@ -94,33 +94,12 @@ describe('Karpenter installation', () => { expect(values['Fn::Join'][1][0]).toContain('{\"foo.key\":\"foo.value\"'); }); - it('should install from old URL if Karpenter version < v0.17.0', () => { - const app = new cdk.App(); - const stack = new cdk.Stack(app, 'test-stack'); - - const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_23, - }); - - // Create Karpenter install with non-default version - new Karpenter(stack, 'Karpenter', { - cluster: cluster, - version: 'v0.6.0', - }); - - const t = Template.fromStack(stack); - t.hasResource('Custom::AWSCDK-EKS-Cluster', {}); - t.hasResourceProperties('Custom::AWSCDK-EKS-HelmChart', { - Repository: Match.stringLikeRegexp('https://charts.karpenter.sh'), - }); - }); - it('should use existing nodeRole instead of creating a new role', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_24, + version: KubernetesVersion.V1_27, }); const preexistingRole = new Role(stack, 'PreExistingRole', { @@ -151,7 +130,7 @@ describe('Karpenter installation', () => { const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_24, + version: KubernetesVersion.V1_27, }); // Create Karpenter install with non-default version @@ -200,13 +179,36 @@ describe('Karpenter installation', () => { ]), })); }); +}); + +describe('Karpenter Versions', () => { + it('should install from old URL if Karpenter version < v0.17.0', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test-stack'); + + const cluster = new Cluster(stack, 'testcluster', { + version: KubernetesVersion.V1_27, + }); + + // Create Karpenter install with non-default version + new Karpenter(stack, 'Karpenter', { + cluster: cluster, + version: 'v0.6.0', + }); + + const t = Template.fromStack(stack); + t.hasResource('Custom::AWSCDK-EKS-Cluster', {}); + t.hasResourceProperties('Custom::AWSCDK-EKS-HelmChart', { + Repository: Match.stringLikeRegexp('https://charts.karpenter.sh'), + }); + }); it('should install from new URL if Karpenter version >= v0.17.0', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_23, + version: KubernetesVersion.V1_27, }); // Create Karpenter install with non-default version @@ -227,7 +229,7 @@ describe('Karpenter installation', () => { const stack = new cdk.Stack(app, 'test-stack'); const cluster = new Cluster(stack, 'testcluster', { - version: KubernetesVersion.V1_23, + version: KubernetesVersion.V1_27, }); // Create Karpenter install with non-default version @@ -248,3 +250,52 @@ describe('Karpenter installation', () => { expect(values['Fn::Join'][1]).toContain('\"}},\"settings\":{\"aws\":{\"clusterName\":\"'); }); }); + +describe('Kubernetes ServiceAccount', () => { + it('should create SA named: karpenter (default)', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test-stack'); + + const cluster = new Cluster(stack, 'testcluster', { + version: KubernetesVersion.V1_27, + }); + + new Karpenter(stack, 'Karpenter', { + cluster: cluster, + }); + + const t = Template.fromStack(stack); + // Test if we have created a ServiceAccount + const valueCapture = new Capture(); + t.hasResourceProperties('Custom::AWSCDK-EKS-HelmChart', { + Values: valueCapture, + }); + + const values = valueCapture.asObject(); + expect(values['Fn::Join'][1][0]).toContain('{\"serviceAccount\":{\"create\":false,\"name\":\"karpenter\"'); + }); + + it('should create SA named: custom-sa', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test-stack'); + + const cluster = new Cluster(stack, 'testcluster', { + version: KubernetesVersion.V1_27, + }); + + new Karpenter(stack, 'Karpenter', { + cluster: cluster, + serviceAccountName: 'custom-sa', + }); + + const t = Template.fromStack(stack); + // Test if we have created a ServiceAccount + const valueCapture = new Capture(); + t.hasResourceProperties('Custom::AWSCDK-EKS-HelmChart', { + Values: valueCapture, + }); + + const values = valueCapture.asObject(); + expect(values['Fn::Join'][1][0]).toContain('{\"serviceAccount\":{\"create\":false,\"name\":\"custom-sa\"'); + }); +});