packages/@aws-cdk/aws-eks-v2-alpha/lib/alb-controller.ts (124 lines of code) (raw):
import * as fs from 'fs';
import * as path from 'path';
import { Construct } from 'constructs';
import { Cluster } from './cluster';
import { HelmChart } from './helm-chart';
import { ServiceAccount } from './service-account';
import * as iam from 'aws-cdk-lib/aws-iam';
// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
import { Aws, Duration, Names, Stack } from 'aws-cdk-lib/core';
/**
* Controller version.
*
* Corresponds to the image tag of 'amazon/aws-load-balancer-controller' image.
*/
export class AlbControllerVersion {
/**
* v2.0.0
*/
public static readonly V2_0_0 = new AlbControllerVersion('v2.0.0', '1.4.1', false);
/**
* v2.0.1
*/
public static readonly V2_0_1 = new AlbControllerVersion('v2.0.1', '1.4.1', false);
/**
* v2.1.0
*/
public static readonly V2_1_0 = new AlbControllerVersion('v2.1.0', '1.4.1', false);
/**
* v2.1.1
*/
public static readonly V2_1_1 = new AlbControllerVersion('v2.1.1', '1.4.1', false);
/**
* v2.1.2
*/
public static readonly V2_1_2 = new AlbControllerVersion('v2.1.2', '1.4.1', false);
/**
* v2.1.3
*/
public static readonly V2_1_3 = new AlbControllerVersion('v2.1.3', '1.4.1', false);
/**
* v2.0.0
*/
public static readonly V2_2_0 = new AlbControllerVersion('v2.2.0', '1.4.1', false);
/**
* v2.2.1
*/
public static readonly V2_2_1 = new AlbControllerVersion('v2.2.1', '1.4.1', false);
/**
* v2.2.2
*/
public static readonly V2_2_2 = new AlbControllerVersion('v2.2.2', '1.4.1', false);
/**
* v2.2.3
*/
public static readonly V2_2_3 = new AlbControllerVersion('v2.2.3', '1.4.1', false);
/**
* v2.2.4
*/
public static readonly V2_2_4 = new AlbControllerVersion('v2.2.4', '1.4.1', false);
/**
* v2.3.0
*/
public static readonly V2_3_0 = new AlbControllerVersion('v2.3.0', '1.4.1', false);
/**
* v2.3.1
*/
public static readonly V2_3_1 = new AlbControllerVersion('v2.3.1', '1.4.1', false);
/**
* v2.4.1
*/
public static readonly V2_4_1 = new AlbControllerVersion('v2.4.1', '1.4.1', false);
/**
* v2.4.2
*/
public static readonly V2_4_2 = new AlbControllerVersion('v2.4.2', '1.4.3', false);
/**
* v2.4.3
*/
public static readonly V2_4_3 = new AlbControllerVersion('v2.4.3', '1.4.4', false);
/**
* v2.4.4
*/
public static readonly V2_4_4 = new AlbControllerVersion('v2.4.4', '1.4.5', false);
/**
* v2.4.5
*/
public static readonly V2_4_5 = new AlbControllerVersion('v2.4.5', '1.4.6', false);
/**
* v2.4.6
*/
public static readonly V2_4_6 = new AlbControllerVersion('v2.4.6', '1.4.7', false);
/**
* v2.4.7
*/
public static readonly V2_4_7 = new AlbControllerVersion('v2.4.7', '1.4.8', false);
/**
* v2.5.0
*/
public static readonly V2_5_0 = new AlbControllerVersion('v2.5.0', '1.5.0', false);
/**
* v2.5.1
*/
public static readonly V2_5_1 = new AlbControllerVersion('v2.5.1', '1.5.2', false);
/**
* v2.5.2
*/
public static readonly V2_5_2 = new AlbControllerVersion('v2.5.2', '1.5.3', false);
/**
* v2.5.3
*/
public static readonly V2_5_3 = new AlbControllerVersion('v2.5.3', '1.5.4', false);
/**
* v2.5.4
*/
public static readonly V2_5_4 = new AlbControllerVersion('v2.5.4', '1.5.5', false);
/**
* v2.6.0
*/
public static readonly V2_6_0 = new AlbControllerVersion('v2.6.0', '1.6.0', false);
/**
* v2.6.1
*/
public static readonly V2_6_1 = new AlbControllerVersion('v2.6.1', '1.6.1', false);
/**
* v2.6.2
*/
public static readonly V2_6_2 = new AlbControllerVersion('v2.6.2', '1.6.2', false);
/**
* v2.7.0
*/
public static readonly V2_7_0 = new AlbControllerVersion('v2.7.0', '1.7.0', false);
/**
* v2.7.1
*/
public static readonly V2_7_1 = new AlbControllerVersion('v2.7.1', '1.7.1', false);
/**
* v2.7.2
*/
public static readonly V2_7_2 = new AlbControllerVersion('v2.7.2', '1.7.2', false);
/**
* v2.8.0
*/
public static readonly V2_8_0 = new AlbControllerVersion('v2.8.0', '1.8.0', false);
/**
* v2.8.1
*/
public static readonly V2_8_1 = new AlbControllerVersion('v2.8.1', '1.8.1', false);
/**
* v2.8.2
*/
public static readonly V2_8_2 = new AlbControllerVersion('v2.8.2', '1.8.2', false);
/**
* Specify a custom version and an associated helm chart version.
* Use this if the version you need is not available in one of the predefined versions.
* Note that in this case, you will also need to provide an IAM policy in the controller options.
*
* ALB controller version and helm chart version compatibility information can be found
* here: https://github.com/aws/eks-charts/blob/v0.0.133/stable/aws-load-balancer-controller/Chart.yaml
*
* @param version The version number.
* @param helmChartVersion The version of the helm chart. Version 1.4.1 is the default version to support legacy
* users.
*/
public static of(version: string, helmChartVersion: string = '1.4.1') {
return new AlbControllerVersion(version, helmChartVersion, true);
}
private constructor(
/**
* The version string.
*/
public readonly version: string,
/**
* The version of the helm chart to use.
*/
public readonly helmChartVersion: string,
/**
* Whether or not its a custom version.
*/
public readonly custom: boolean) { }
}
/**
* ALB Scheme.
*
* @see https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.3/guide/ingress/annotations/#scheme
*/
export enum AlbScheme {
/**
* The nodes of an internal load balancer have only private IP addresses.
* The DNS name of an internal load balancer is publicly resolvable to the private IP addresses of the nodes.
* Therefore, internal load balancers can only route requests from clients with access to the VPC for the load balancer.
*/
INTERNAL = 'internal',
/**
* An internet-facing load balancer has a publicly resolvable DNS name, so it can route requests from clients over the internet
* to the EC2 instances that are registered with the load balancer.
*/
INTERNET_FACING = 'internet-facing',
}
/**
* Options for `AlbController`.
*/
export interface AlbControllerOptions {
/**
* Version of the controller.
*/
readonly version: AlbControllerVersion;
/**
* The repository to pull the controller image from.
*
* Note that the default repository works for most regions, but not all.
* If the repository is not applicable to your region, use a custom repository
* according to the information here: https://github.com/kubernetes-sigs/aws-load-balancer-controller/releases.
*
* @default '602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller'
*/
readonly repository?: string;
/**
* The IAM policy to apply to the service account.
*
* If you're using one of the built-in versions, this is not required since
* CDK ships with the appropriate policies for those versions.
*
* However, if you are using a custom version, this is required (and validated).
*
* @default - Corresponds to the predefined version.
*/
readonly policy?: any;
}
/**
* Properties for `AlbController`.
*/
export interface AlbControllerProps extends AlbControllerOptions {
/**
* [disable-awslint:ref-via-interface]
* Cluster to install the controller onto.
*/
readonly cluster: Cluster;
}
/**
* Construct for installing the AWS ALB Contoller on EKS clusters.
*
* Use the factory functions `get` and `getOrCreate` to obtain/create instances of this controller.
*
* @see https://kubernetes-sigs.github.io/aws-load-balancer-controller
*
*/
export class AlbController extends Construct {
/**
* Create the controller construct associated with this cluster and scope.
*
* Singleton per stack/cluster.
*/
public static create(scope: Construct, props: AlbControllerProps) {
const stack = Stack.of(scope);
const uid = AlbController.uid(props.cluster);
return new AlbController(stack, uid, props);
}
private static uid(cluster: Cluster) {
return `${Names.nodeUniqueId(cluster.node)}-AlbController`;
}
public constructor(scope: Construct, id: string, props: AlbControllerProps) {
super(scope, id);
const namespace = 'kube-system';
const serviceAccount = new ServiceAccount(this, 'alb-sa', { namespace, name: 'aws-load-balancer-controller', cluster: props.cluster });
if (props.version.custom && !props.policy) {
throw new Error("'albControllerOptions.policy' is required when using a custom controller version");
}
// https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/deploy/installation/#iam-permissions
const policy: any = props.policy ?? JSON.parse(fs.readFileSync(path.join(__dirname, 'addons', `alb-iam_policy-${props.version.version}.json`), 'utf8'));
for (const statement of policy.Statement) {
const rewrittenStatement = {
...statement,
Resource: this.rewritePolicyResources(statement.Resource),
};
serviceAccount.addToPrincipalPolicy(iam.PolicyStatement.fromJson(rewrittenStatement));
}
// https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/deploy/installation/#add-controller-to-cluster
const chart = new HelmChart(this, 'Resource', {
cluster: props.cluster,
chart: 'aws-load-balancer-controller',
repository: 'https://aws.github.io/eks-charts',
namespace,
release: 'aws-load-balancer-controller',
version: props.version.helmChartVersion,
wait: true,
timeout: Duration.minutes(15),
values: {
clusterName: props.cluster.clusterName,
serviceAccount: {
create: false,
name: serviceAccount.serviceAccountName,
},
region: Stack.of(this).region,
vpcId: props.cluster.vpc.vpcId,
image: {
repository: props.repository ?? '602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller',
tag: props.version.version,
},
},
});
// the controller relies on permissions deployed using these resources.
chart.node.addDependency(serviceAccount);
chart.node.addDependency(props.cluster.openIdConnectProvider);
}
private rewritePolicyResources(resources: string | string[] | undefined): string | string[] | undefined {
// This is safe to disable because we're actually replacing the literal partition with a reference to
// the stack partition (which is hardcoded into the JSON files) to prevent issues such as
// aws/aws-cdk#22520.
// eslint-disable-next-line @cdklabs/no-literal-partition
const rewriteResource = (s: string) => s.replace('arn:aws:', `arn:${Aws.PARTITION}:`);
if (!resources) {
return resources;
}
if (!Array.isArray(resources)) {
return rewriteResource(resources);
}
return resources.map(rewriteResource);
}
}