Skip to content

Commit 86ce155

Browse files
authored
feat(msk): support ServerlessCluster (#32780)
### Issue # (if applicable) Closes #28709. ### Reason for this change L2 Construct for MSK Serverless is not supported. ### Description of changes Add `ServerlessCluster` class. ### Describe any new or updated permissions being added Nothing ### Description of how you validated changes Add unit tests and integ test. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f004c7e commit 86ce155

15 files changed

+3340
-1
lines changed

packages/@aws-cdk/aws-msk-alpha/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,24 @@ const cluster = new msk.Cluster(this, 'cluster', {
231231
storageMode: msk.StorageMode.TIERED,
232232
});
233233
```
234+
235+
## MSK Serverless
236+
237+
You can also use MSK Serverless by using `ServerlessCluster` class.
238+
239+
MSK Serverless is a cluster type for Amazon MSK that makes it possible for you to run Apache Kafka without having to manage and scale cluster capacity.
240+
241+
MSK Serverless requires IAM access control for all clusters.
242+
243+
For more infomation, see [Use MSK Serverless clusters](https://docs.aws.amazon.com/msk/latest/developerguide/serverless-getting-started.html).
244+
245+
```ts
246+
declare const vpc: ec2.Vpc;
247+
248+
const serverlessCluster = new msk.ServerlessCluster(this, 'ServerlessCluster', {
249+
clusterName: 'MyServerlessCluster',
250+
vpcConfigs: [
251+
{ vpc },
252+
],
253+
});
254+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"exclude": [
3+
"props-physical-name:@aws-cdk/aws-msk-alpha.ServerlessClusterProps"
34
]
45
}

packages/@aws-cdk/aws-msk-alpha/lib/cluster.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export interface ICluster extends core.IResource, ec2.IConnectable {
3737
/**
3838
* A new or imported MSK Cluster.
3939
*/
40-
abstract class ClusterBase extends core.Resource implements ICluster {
40+
export abstract class ClusterBase extends core.Resource implements ICluster {
4141
public abstract readonly clusterArn: string;
4242
public abstract readonly clusterName: string;
4343
/** @internal */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './cluster';
22
export * from './cluster-version';
3+
export * from './serverless-cluster';
34

45
// AWS::MSK CloudFormation Resources:
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
2+
import { Fn, Lazy, Names } from 'aws-cdk-lib';
3+
import * as constructs from 'constructs';
4+
import { ClusterBase, ICluster } from '.';
5+
import { CfnServerlessCluster } from 'aws-cdk-lib/aws-msk';
6+
7+
/**
8+
* Properties for a MSK Serverless Cluster
9+
*/
10+
export interface ServerlessClusterProps {
11+
/**
12+
* The physical name of the cluster.
13+
*
14+
* @default - auto generate
15+
*/
16+
readonly clusterName?: string;
17+
18+
/**
19+
* The configuration of the Amazon VPCs for the cluster.
20+
* You can specify up to 5 VPC configurations.
21+
*/
22+
readonly vpcConfigs: VpcConfig[];
23+
}
24+
25+
/**
26+
* The configuration of the Amazon VPCs for the cluster.
27+
*/
28+
export interface VpcConfig {
29+
/**
30+
* Defines the virtual networking environment for this cluster.
31+
* Must have at least 2 subnets in two different AZs.
32+
*/
33+
readonly vpc: ec2.IVpc;
34+
35+
/**
36+
* The subnets associated with the cluster.
37+
*
38+
* @default - the Vpc default strategy if not specified.
39+
*/
40+
readonly vpcSubnets?: ec2.SubnetSelection;
41+
42+
/**
43+
* The security groups associated with the cluster.
44+
* You can specify up to 5 security groups.
45+
*
46+
* @default - create new security group
47+
*/
48+
readonly securityGroups?: ec2.ISecurityGroup[];
49+
}
50+
51+
/**
52+
* Create a MSK Serverless Cluster.
53+
*
54+
* @resource AWS::MSK::ServerlessCluster
55+
*/
56+
export class ServerlessCluster extends ClusterBase {
57+
/**
58+
* Reference an existing cluster, defined outside of the CDK code, by name.
59+
*/
60+
public static fromClusterArn(
61+
scope: constructs.Construct,
62+
id: string,
63+
clusterArn: string,
64+
): ICluster {
65+
class Import extends ClusterBase {
66+
public readonly clusterArn = clusterArn;
67+
public readonly clusterName = Fn.select(1, Fn.split('/', clusterArn)); // ['arn:partition:kafka:region:account-id', clusterName, clusterId]
68+
}
69+
70+
return new Import(scope, id);
71+
}
72+
73+
public readonly clusterArn: string;
74+
public readonly clusterName: string;
75+
76+
private _securityGroups: ec2.ISecurityGroup[] = [];
77+
78+
constructor(scope: constructs.Construct, id: string, props: ServerlessClusterProps) {
79+
super(scope, id, {
80+
physicalName: props.clusterName ??
81+
Lazy.string({
82+
produce: () => Names.uniqueResourceName(this, { maxLength: 64 }),
83+
}),
84+
});
85+
86+
if (props.vpcConfigs.length < 1 || props.vpcConfigs.length > 5) {
87+
throw Error(`\`vpcConfigs\` must contain between 1 and 5 configurations, got ${props.vpcConfigs.length} configurations.`);
88+
}
89+
90+
const vpcConfigs = props.vpcConfigs.map((vpcConfig, index) => this._renderVpcConfig(vpcConfig, index));
91+
92+
this._connections = new ec2.Connections({
93+
securityGroups: this._securityGroups,
94+
});
95+
96+
/**
97+
* TODO At the time of implementation, MSK Serverless only supports IAM authentication, so it cannot be disabled.
98+
* If it becomes configurable in the future, the property will need to be exposed.
99+
*
100+
* @see https://docs.aws.amazon.com/msk/latest/developerguide/serverless.html
101+
*/
102+
const resource = new CfnServerlessCluster(this, 'Resource', {
103+
clusterName: this.physicalName,
104+
clientAuthentication: {
105+
sasl: {
106+
iam: {
107+
enabled: true,
108+
},
109+
},
110+
},
111+
vpcConfigs,
112+
});
113+
114+
this.clusterName = this.getResourceNameAttribute(
115+
Fn.select(1, Fn.split('/', resource.ref)),
116+
);
117+
this.clusterArn = resource.ref;
118+
}
119+
120+
/**
121+
* Render Vpc Config property
122+
*/
123+
private _renderVpcConfig(vpcConfig: VpcConfig, index: number): CfnServerlessCluster.VpcConfigProperty {
124+
const subnetSelection = vpcConfig.vpc.selectSubnets(vpcConfig.vpcSubnets);
125+
126+
if (subnetSelection.subnets.length < 2) {
127+
throw Error(
128+
`Cluster requires at least 2 subnets, got ${subnetSelection.subnets.length} subnet.`,
129+
);
130+
}
131+
132+
let securityGroups: ec2.ISecurityGroup[] = [];
133+
134+
if (vpcConfig.securityGroups) {
135+
if (vpcConfig.securityGroups.length < 1 || vpcConfig.securityGroups.length > 5) {
136+
throw Error(`\`securityGroups\` must contain between 1 and 5 elements, got ${vpcConfig.securityGroups.length} elements.`);
137+
}
138+
securityGroups = vpcConfig.securityGroups;
139+
} else {
140+
securityGroups.push(new ec2.SecurityGroup(this, `SecurityGroup-${index}`, {
141+
description: 'MSK Serverless security group',
142+
vpc: vpcConfig.vpc,
143+
}));
144+
}
145+
146+
this._securityGroups.push(...securityGroups);
147+
148+
return {
149+
subnetIds: subnetSelection.subnets.map((subnet) => subnet.subnetId),
150+
securityGroups: securityGroups?.map((securityGroup) => securityGroup.securityGroupId),
151+
};
152+
}
153+
}

packages/@aws-cdk/aws-msk-alpha/test/integ.serverless-cluster.js.snapshot/cdk.out

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/aws-msk-alpha/test/integ.serverless-cluster.js.snapshot/integ.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)