diff --git a/.projen/deps.json b/.projen/deps.json index f6dde164..3c81d8b0 100644 --- a/.projen/deps.json +++ b/.projen/deps.json @@ -13,6 +13,11 @@ "version": "^27", "type": "build" }, + { + "name": "@types/lodash", + "version": "^4", + "type": "build" + }, { "name": "@types/node", "version": "^14", @@ -134,6 +139,11 @@ { "name": "aws-cdk-lib", "type": "runtime" + }, + { + "name": "lodash", + "version": "^4", + "type": "runtime" } ], "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." diff --git a/.projenrc.js b/.projenrc.js index 2b2b9fd8..27a2f9db 100644 --- a/.projenrc.js +++ b/.projenrc.js @@ -26,8 +26,8 @@ const project = new awscdk.AwsCdkConstructLibrary({ autoApproveProjenUpgrades: true, projenTokenSecret: 'PROJEN_GITHUB_TOKEN', autoApproveUpgrades: true, - deps: ['aws-cdk-lib', '@aws-cdk/lambda-layer-kubectl-v23', '@aws-cdk/lambda-layer-kubectl-v24'], - devDeps: ['aws-cdk-lib', '@aws-cdk/lambda-layer-kubectl-v23', '@aws-cdk/lambda-layer-kubectl-v24'], + deps: ['aws-cdk-lib', '@aws-cdk/lambda-layer-kubectl-v23', '@aws-cdk/lambda-layer-kubectl-v24', 'lodash@^4'], + devDeps: ['aws-cdk-lib', '@aws-cdk/lambda-layer-kubectl-v23', '@aws-cdk/lambda-layer-kubectl-v24', '@types/lodash@^4'], // deps: [], /* Runtime dependencies of this module. */ // devDeps: [], /* Build dependencies for this module. */ // packageName: undefined, /* The "name" in package.json. */ diff --git a/package.json b/package.json index 6e07e6db..208894c0 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@aws-cdk/lambda-layer-kubectl-v23": "^2.0.3", "@aws-cdk/lambda-layer-kubectl-v24": "^2.0.89", "@types/jest": "^27", + "@types/lodash": "^4", "@types/node": "^14", "@typescript-eslint/eslint-plugin": "^5", "@typescript-eslint/parser": "^5", @@ -66,7 +67,8 @@ "dependencies": { "@aws-cdk/lambda-layer-kubectl-v23": "^2.0.3", "@aws-cdk/lambda-layer-kubectl-v24": "^2.0.89", - "aws-cdk-lib": "^2.63.2" + "aws-cdk-lib": "^2.63.2", + "lodash": "^4" }, "keywords": [ "aws", diff --git a/src/karpenter.ts b/src/karpenter.ts index 6db18136..5479537e 100644 --- a/src/karpenter.ts +++ b/src/karpenter.ts @@ -6,8 +6,10 @@ import { SqsQueue } from 'aws-cdk-lib/aws-events-targets'; import { CfnInstanceProfile, ManagedPolicy, OpenIdConnectPrincipal, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { Queue } from 'aws-cdk-lib/aws-sqs'; import { Construct } from 'constructs'; +import { merge } from 'lodash'; import { TagSubnetsCustomResource } from './custom-resource'; + export interface KarpenterProps { /** * The EKS cluster on which Karpenter is going to be installed on. @@ -24,6 +26,14 @@ export interface KarpenterProps { * If left blank, private VPC subnets will be used and tagged by default. */ readonly subnets?: ISubnet[]; + + /** + * Optional map of values to pass to the Karpenter helm chart. + * This will be merged into the default values that setup AWS related values. + */ + readonly helmValues?: { + [key: string]: any; + }; } export interface ProvisionerSpecs { @@ -81,8 +91,14 @@ export interface ProvisionerSpecs { * AWS cloud provider configuration. */ readonly provider?: ProviderProps; + + /** + * Optional callback to perform final modifications on the Provisoner resource definition. + */ + readonly finalizeProvisioner?: {(r:Record): void}; } + export interface ProvisionerReqs { /** * Instance types to be used by the Karpenter Provider. @@ -129,6 +145,11 @@ export interface ProviderProps { * and Karpenter will use the latest EKS-optimized AMIs if an amiSelector is not specified. */ readonly amiSelector?: {[key: string]: string}; + + /** + * Optionall callback to perform final modifications on the node template resource definition. + */ + readonly finalizeProvider?: {(r:Record): void}; } export interface Limits { @@ -469,17 +490,8 @@ export class Karpenter extends Construct { this.karpenterControllerRole.addManagedPolicy(this.karpenterControllerPolicy); - this.karpenterHelmChart = new HelmChart(this, 'KarpenterHelmChart', { - chart: 'karpenter', - createNamespace: true, - version: 'v0.23.0', - cluster: this.cluster, - namespace: 'karpenter', - release: 'karpenter', - repository: 'oci://public.ecr.aws/karpenter/karpenter', - timeout: Duration.minutes(15), - wait: true, - values: { + const helm_values: {[key: string]: any} = merge( + { serviceAccount: { annotations: { 'eks.amazonaws.com/role-arn': this.karpenterControllerRole.roleArn, @@ -496,6 +508,20 @@ export class Karpenter extends Construct { }, }, }, + props?.helmValues ?? {}, + ); + + this.karpenterHelmChart = new HelmChart(this, 'KarpenterHelmChart', { + chart: 'karpenter', + createNamespace: true, + version: 'v0.23.0', + cluster: this.cluster, + namespace: 'karpenter', + release: 'karpenter', + repository: 'oci://public.ecr.aws/karpenter/karpenter', + timeout: Duration.minutes(15), + wait: true, + values: helm_values, }); new CfnOutput(this, 'clusterName', { value: this.cluster.clusterName }); @@ -515,7 +541,7 @@ export class Karpenter extends Construct { // see: https://karpenter.sh/v0.23.0/concepts/provisioners/ // see: https://karpenter.sh/v0.23.0/concepts/node-templates/ const awsNodeTemplateId = `${id}-awsNodeTemplate`.toLowerCase(); - const awsNodeTemplate = this.cluster.addManifest(awsNodeTemplateId, { + const aws_node_template_resource: Record = { apiVersion: 'karpenter.k8s.aws/v1alpha1', kind: 'AWSNodeTemplate', metadata: { @@ -545,13 +571,15 @@ export class Karpenter extends Construct { // TODO: add userData https://karpenter.sh/v0.23.0/aws/provisioning/#userdata // TODO: add metadataOptions https://karpenter.sh/v0.23.0/aws/provisioning/#metadata-options }, - }); + }; + provisionerSpecs?.provider?.finalizeProvider?.(aws_node_template_resource); + const awsNodeTemplate = this.cluster.addManifest(awsNodeTemplateId, aws_node_template_resource); // see: https://karpenter.sh/v0.23.0/concepts/provisioners/#specrequirements const requirements = this.setRequirements(provisionerSpecs?.requirements); // see: https://karpenter.sh/v0.23.0/concepts/provisioners/ - const provisioner = this.cluster.addManifest(id, { + const provisioner_resource: Record = { apiVersion: 'karpenter.sh/v1alpha5', kind: 'Provisioner', metadata: { @@ -592,7 +620,9 @@ export class Karpenter extends Construct { // see: https://karpenter.sh/v0.23.0/concepts/provisioners/#specproviderref }, - }); + }; + provisionerSpecs?.finalizeProvisioner?.(provisioner_resource); + const provisioner = this.cluster.addManifest(id, provisioner_resource); provisioner.node.addDependency(awsNodeTemplate); awsNodeTemplate.node.addDependency(this.karpenterHelmChart); diff --git a/test/karpenter.test.ts b/test/karpenter.test.ts index c97a3072..a10d2a6d 100644 --- a/test/karpenter.test.ts +++ b/test/karpenter.test.ts @@ -9,6 +9,9 @@ const { stack, vpc, cluster } = testFixtureCluster(); const karpenter = new Karpenter(stack, 'karpenter', { cluster, vpc, + helmValues: { + replicas: 1, + }, }); karpenter.addProvisioner('default'); @@ -66,6 +69,11 @@ karpenter.addProvisioner('custom', { }, ], }, + /* + finalizeProvisioner: (r) => { + r.spec.weight = 10; + }, + */ }); test('has karpenter controller policy', () => { diff --git a/yarn.lock b/yarn.lock index 2664a679..5bf59664 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,20 +10,20 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@aws-cdk/asset-awscli-v1@^2.2.52": - version "2.2.58" - resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.58.tgz#8974c87cc156a8b515defd804be057ffe941e783" - integrity sha512-VZ91QsvDzL916SiQJ7A7gwWW9WcoVQRaKXr0ELt/0uRmeT2q6f/ZB4qED6wc+1DmxTN4Fc2dboFl3eUz7vSf9g== +"@aws-cdk/asset-awscli-v1@^2.2.65": + version "2.2.70" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.70.tgz#23c39879ca5c66ffd97f5990e6a8493c9488b7f7" + integrity sha512-Ni6z15ucIoTiAZEZCCs7EuvdVFza8q0kfnLb42X1fSmGORUVOiWiuO9VUgJpURe9VfC16sozg4fhiZKnLClCKg== "@aws-cdk/asset-kubectl-v20@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz#d01c1efb867fb7f2cfd8c8b230b8eae16447e156" integrity sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw== -"@aws-cdk/asset-node-proxy-agent-v5@^2.0.42": - version "2.0.47" - resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.47.tgz#134e198b7d728c95073f951d285c082034ea2001" - integrity sha512-RbDlsUgPqv2iCXfQEWdOlaelMqKNEpwweFg1L8VTK8NbF7iKbW2L461FzapWSpl2YSq/Gu1LAGCDYGyV4r1Lsg== +"@aws-cdk/asset-node-proxy-agent-v5@^2.0.54": + version "2.0.59" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.59.tgz#9c00a212ccdd64507aee985acc5e574ea533d166" + integrity sha512-SbHIZsDe+Ore64tkJjlaSvyWdlq/NTOhIvZ0KnhsRPLaKV6c3SeWsK6l5w3l6YSzsOcw8COSrruVsE8X0WhUBA== "@aws-cdk/lambda-layer-kubectl-v23@^2.0.3": version "2.0.3" @@ -870,6 +870,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash@^4": + version "4.14.191" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" + integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== + "@types/minimatch@^5.1.2": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -1254,13 +1259,13 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== aws-cdk-lib@^2.63.2: - version "2.63.2" - resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.63.2.tgz#c5308cc9b81ec2179ddc37089772d83e07d7c595" - integrity sha512-Kk9aP79O+9UJ3Bw0wjquMD+RpWPQ/Fa4wUcP5mdpROpoipPXsFFZPTXT8j8kp2oFOZa9OHxacTF06+/M0H/bWQ== + version "2.65.0" + resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.65.0.tgz#0f709f1aaa7cfd8029d8b017bec1e6a4c15fabd9" + integrity sha512-yTeBe9+Li8ZHZICJc0y1uarbSruc3KIXP1KC7tjVrkg+ye7EE8eJTZm7+PpJd6O0ciPinn/k/dE9gHwUImygGA== dependencies: - "@aws-cdk/asset-awscli-v1" "^2.2.52" + "@aws-cdk/asset-awscli-v1" "^2.2.65" "@aws-cdk/asset-kubectl-v20" "^2.1.1" - "@aws-cdk/asset-node-proxy-agent-v5" "^2.0.42" + "@aws-cdk/asset-node-proxy-agent-v5" "^2.0.54" "@balena/dockerignore" "^1.0.2" case "1.6.3" fs-extra "^9.1.0" @@ -4226,7 +4231,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.15, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==