From 533caaeaaa2b1def8e312efe0fa9a7734076d43a Mon Sep 17 00:00:00 2001 From: Alexander Zarubin Date: Thu, 14 Mar 2024 18:47:31 +0200 Subject: [PATCH] constructs/Service - add ability to use existing Cluster (#3692) * constructs/Service - add ability to use existing Cluster * added cluster test for constructs/Service * cleanup * Move cluster to cdk.cluster * Sync --------- Co-authored-by: Frank --- .changeset/rotten-plants-yell.md | 6 +++ packages/sst/src/constructs/Service.ts | 45 ++++++++++++++------ packages/sst/test/constructs/Service.test.ts | 20 ++++++++- www/docs/constructs/Service.about.md | 20 +++++++++ www/generate.mjs | 1 + 5 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 .changeset/rotten-plants-yell.md diff --git a/.changeset/rotten-plants-yell.md b/.changeset/rotten-plants-yell.md new file mode 100644 index 0000000000..419cfbd159 --- /dev/null +++ b/.changeset/rotten-plants-yell.md @@ -0,0 +1,6 @@ +--- +"sst": patch +"@sst/docs": patch +--- + +Service: support using existing ECS Cluster diff --git a/packages/sst/src/constructs/Service.ts b/packages/sst/src/constructs/Service.ts index 8cab53a200..e0a0010940 100644 --- a/packages/sst/src/constructs/Service.ts +++ b/packages/sst/src/constructs/Service.ts @@ -63,6 +63,7 @@ import { FargateService, FargateTaskDefinition, FargateServiceProps, + ICluster, } from "aws-cdk-lib/aws-ecs"; import { LogGroup, LogRetention, RetentionDays } from "aws-cdk-lib/aws-logs"; import { Platform } from "aws-cdk-lib/aws-ecr-assets"; @@ -563,7 +564,22 @@ export interface ServiceProps { image?: ContainerDefinitionOptions["image"]; }; /** - * Runs codebuild job in the specified VPC. Note this will only work once deployed. + * Create the service in an existing ECS cluster. + * + * @example + * ```js + * import { Cluster } from "aws-cdk-lib/aws-ecs"; + * + * { + * cdk: { + * cluster: Cluster.fromClusterArn(stack, "Cluster", "arn:aws:ecs:us-east-1:123456789012:cluster/my-cluster"), + * } + * } + * ``` + */ + cluster?: ICluster; + /** + * Create the service in the specified VPC. Note this will only work once deployed. * * @example * ```js @@ -608,7 +624,7 @@ export class Service extends Construct implements SSTConstruct { private doNotDeploy: boolean; private devFunction?: Function; private vpc?: IVpc; - private cluster?: Cluster; + private cluster?: ICluster; private container?: ContainerDefinition; private taskDefinition?: FargateTaskDefinition; private service?: FargateService; @@ -646,8 +662,8 @@ export class Service extends Construct implements SSTConstruct { // Create ECS cluster const vpc = this.createVpc(); - const { cluster, container, taskDefinition, service } = - this.createService(vpc); + const cluster = this.createCluster(vpc); + const { container, taskDefinition, service } = this.createService(cluster); const { alb, target } = this.createLoadBalancer(vpc, service); this.createAutoScaling(service, target); this.alb = alb; @@ -929,14 +945,24 @@ export class Service extends Construct implements SSTConstruct { ); } - private createService(vpc: IVpc) { + private createCluster(vpc: IVpc) { + if (this.props.cdk?.cluster) return this.props.cdk.cluster; + + const app = this.node.root as App; + const clusterName = app.logicalPrefixedName(this.node.id); + return new Cluster(this, "Cluster", { + clusterName, + vpc, + }); + } + + private createService(cluster: ICluster) { const { architecture, cpu, memory, storage, port, logRetention, cdk } = this.props; const app = this.node.root as App; - const clusterName = app.logicalPrefixedName(this.node.id); const logGroup = new LogRetention(this, "LogRetention", { - logGroupName: `/sst/service/${clusterName}`, + logGroupName: `/sst/service/${cluster.clusterName}`, retention: RetentionDays[logRetention.toUpperCase() as keyof typeof RetentionDays], logRetentionRetryOptions: { @@ -944,11 +970,6 @@ export class Service extends Construct implements SSTConstruct { }, }); - const cluster = new Cluster(this, "Cluster", { - clusterName, - vpc, - }); - const ephemeralStorageGiB = toCdkSize(storage).toGibibytes(); const taskDefinition = new FargateTaskDefinition(this, `TaskDefinition`, { // @ts-expect-error diff --git a/packages/sst/test/constructs/Service.test.ts b/packages/sst/test/constructs/Service.test.ts index dade038883..fe0df37b92 100644 --- a/packages/sst/test/constructs/Service.test.ts +++ b/packages/sst/test/constructs/Service.test.ts @@ -1,6 +1,6 @@ import { test, expect, beforeAll, vi } from "vitest"; import { HostedZone } from "aws-cdk-lib/aws-route53"; -import { ContainerImage } from "aws-cdk-lib/aws-ecs"; +import { ContainerImage, Cluster } from "aws-cdk-lib/aws-ecs"; import { countResources, countResourcesLike, @@ -493,6 +493,24 @@ test("environment", async () => { }); }); +test("cdk.cluster", async () => { + const app = await createApp(); + const stack = new Stack(app, "stack"); + const cluster = new Cluster(stack, "custom-cluster-id", { + clusterName: "custom-cluster", + }); + + new Service(stack, "Service", { + port: 3000, + cdk: { cluster }, + }); + + countResources(stack, "AWS::ECS::Cluster", 1); + hasResource(stack, "AWS::ECS::Cluster", { + ClusterName: "custom-cluster", + }); +}); + test("cdk.fargateService", async () => { const { stack } = await createService({ cdk: { diff --git a/www/docs/constructs/Service.about.md b/www/docs/constructs/Service.about.md index d8c79c7385..3b115aa9e5 100644 --- a/www/docs/constructs/Service.about.md +++ b/www/docs/constructs/Service.about.md @@ -423,3 +423,23 @@ new Service(stack, "MyService", { }, }); ``` + +### Using an existing Cluster + +```js +import { Cluster } from "aws-cdk-lib/aws-ecs"; + +const cluster = new Cluster(stack, "SharedCluster"); + +new Service(stack, "MyServiceA", { + path: "./service-a", + port: 3000, + cdk: { cluster }, +}); + +new Service(stack, "MyServiceB", { + path: "./service-b", + port: 3000, + cdk: { cluster }, +}); +``` diff --git a/www/generate.mjs b/www/generate.mjs index af374ce187..8c3b8a4038 100644 --- a/www/generate.mjs +++ b/www/generate.mjs @@ -28,6 +28,7 @@ const CDK_DOCS_MAP = { FargateServiceProps: "aws_ecs", FargateTaskDefinition: "aws_ecs", FunctionUrlOptions: "aws_lambda", + ICluster: "aws_ecs", LogGroup: "aws_logs", LogGroupProps: "aws_logs", ILogGroup: "aws_logs",