Skip to content

Commit a7d314c

Browse files
authored
feat(ecs): allow users to provide a CloudMap service to associate with an ECS service (#13192)
This PR introduces `BaseService.associateCloudMapService()` which allows the user to associate the ECS service with a CloudMap service that they provide. **API sample** ```ts const cloudMapService = new cloudmap.Service(...); const ecsService = new ecs.FargateService(...); ecsService.associateCloudMapService({ service: cloudMapService, }); ``` Closes #10057 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent c4dc3bc commit a7d314c

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

packages/@aws-cdk/aws-ecs/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,20 @@ new ecs.Ec2Service(stack, 'Service', {
728728
});
729729
```
730730

731+
### Associate With a Specific CloudMap Service
732+
733+
You may associate an ECS service with a specific CloudMap service. To do
734+
this, use the service's `associateCloudMapService` method:
735+
736+
```ts
737+
const cloudMapService = new cloudmap.Service(...);
738+
const ecsService = new ecs.FargateService(...);
739+
740+
ecsService.associateCloudMapService({
741+
service: cloudMapService,
742+
});
743+
```
744+
731745
## Capacity Providers
732746

733747
Currently, only `FARGATE` and `FARGATE_SPOT` capacity providers are supported.

packages/@aws-cdk/aws-ecs/lib/base/base-service.ts

+47
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,27 @@ export abstract class BaseService extends Resource
601601
return cloudmapService;
602602
}
603603

604+
/**
605+
* Associates this service with a CloudMap service
606+
*/
607+
public associateCloudMapService(options: AssociateCloudMapServiceOptions): void {
608+
const service = options.service;
609+
610+
const { containerName, containerPort } = determineContainerNameAndPort({
611+
taskDefinition: this.taskDefinition,
612+
dnsRecordType: service.dnsRecordType,
613+
container: options.container,
614+
containerPort: options.containerPort,
615+
});
616+
617+
// add Cloudmap service to the ECS Service's serviceRegistry
618+
this.addServiceRegistry({
619+
arn: service.serviceArn,
620+
containerName,
621+
containerPort,
622+
});
623+
}
624+
604625
/**
605626
* This method returns the specified CloudWatch metric name for this service.
606627
*/
@@ -748,6 +769,10 @@ export abstract class BaseService extends Resource
748769
* Associate Service Discovery (Cloud Map) service
749770
*/
750771
private addServiceRegistry(registry: ServiceRegistry) {
772+
if (this.serviceRegistries.length >= 1) {
773+
throw new Error('Cannot associate with the given service discovery registry. ECS supports at most one service registry per service.');
774+
}
775+
751776
const sr = this.renderServiceRegistry(registry);
752777
this.serviceRegistries.push(sr);
753778
}
@@ -816,6 +841,28 @@ export interface CloudMapOptions {
816841
readonly containerPort?: number;
817842
}
818843

844+
/**
845+
* The options for using a cloudmap service.
846+
*/
847+
export interface AssociateCloudMapServiceOptions {
848+
/**
849+
* The cloudmap service to register with.
850+
*/
851+
readonly service: cloudmap.IService;
852+
853+
/**
854+
* The container to point to for a SRV record.
855+
* @default - the task definition's default container
856+
*/
857+
readonly container?: ContainerDefinition;
858+
859+
/**
860+
* The port to point to for a SRV record.
861+
* @default - the default port of the task definition's default container
862+
*/
863+
readonly containerPort?: number;
864+
}
865+
819866
/**
820867
* Service Registry for ECS service
821868
*/

packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts

+95
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,101 @@ nodeunitShim({
262262
test.done();
263263
},
264264

265+
'with user-provided cloudmap service'(test: Test) {
266+
// GIVEN
267+
const stack = new cdk.Stack();
268+
const vpc = new ec2.Vpc(stack, 'MyVpc', {});
269+
const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });
270+
const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef');
271+
272+
const container = taskDefinition.addContainer('web', {
273+
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
274+
memoryLimitMiB: 512,
275+
});
276+
container.addPortMappings({ containerPort: 8000 });
277+
278+
const cloudMapNamespace = new cloudmap.PrivateDnsNamespace(stack, 'TestCloudMapNamespace', {
279+
name: 'scorekeep.com',
280+
vpc,
281+
});
282+
283+
const cloudMapService = new cloudmap.Service(stack, 'Service', {
284+
name: 'service-name',
285+
namespace: cloudMapNamespace,
286+
dnsRecordType: cloudmap.DnsRecordType.SRV,
287+
});
288+
289+
const ecsService = new ecs.FargateService(stack, 'FargateService', {
290+
cluster,
291+
taskDefinition,
292+
});
293+
294+
// WHEN
295+
ecsService.associateCloudMapService({
296+
service: cloudMapService,
297+
container: container,
298+
containerPort: 8000,
299+
});
300+
301+
// THEN
302+
expect(stack).to(haveResource('AWS::ECS::Service', {
303+
ServiceRegistries: [
304+
{
305+
ContainerName: 'web',
306+
ContainerPort: 8000,
307+
RegistryArn: { 'Fn::GetAtt': ['ServiceDBC79909', 'Arn'] },
308+
},
309+
],
310+
}));
311+
312+
test.done();
313+
},
314+
315+
'errors when more than one service registry used'(test: Test) {
316+
// GIVEN
317+
const stack = new cdk.Stack();
318+
const vpc = new ec2.Vpc(stack, 'MyVpc', {});
319+
const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });
320+
const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef');
321+
322+
const container = taskDefinition.addContainer('web', {
323+
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
324+
memoryLimitMiB: 512,
325+
});
326+
container.addPortMappings({ containerPort: 8000 });
327+
328+
const cloudMapNamespace = new cloudmap.PrivateDnsNamespace(stack, 'TestCloudMapNamespace', {
329+
name: 'scorekeep.com',
330+
vpc,
331+
});
332+
333+
const ecsService = new ecs.FargateService(stack, 'FargateService', {
334+
cluster,
335+
taskDefinition,
336+
});
337+
338+
ecsService.enableCloudMap({
339+
cloudMapNamespace,
340+
});
341+
342+
const cloudMapService = new cloudmap.Service(stack, 'Service', {
343+
name: 'service-name',
344+
namespace: cloudMapNamespace,
345+
dnsRecordType: cloudmap.DnsRecordType.SRV,
346+
});
347+
348+
// WHEN / THEN
349+
test.throws(() => {
350+
ecsService.associateCloudMapService({
351+
service: cloudMapService,
352+
container: container,
353+
containerPort: 8000,
354+
});
355+
}, /at most one service registry/i);
356+
357+
test.done();
358+
},
359+
265360
'with all properties set'(test: Test) {
266361
// GIVEN
267362
const stack = new cdk.Stack();

0 commit comments

Comments
 (0)