Skip to content

Commit c968c96

Browse files
authored
fix(cloudmap): fix CloudMap Service import, expose ECS CloudMap Service (#4313)
- Fix the missing initialization of `namespace` on CloudMap's `Service.fromServiceAttributes()`. - Expose the created CloudMap Service on ECS services so that `fromServiceAttributes()` doesn't need to be called at all. - Fix a number of property initialization errors across the library. Fixes #4286. BREAKING CHANGE: `cloudmap.Service.fromServiceAttributes` takes a newly required argument `namespace`.
1 parent 910c8bf commit c968c96

File tree

15 files changed

+149
-84
lines changed

15 files changed

+149
-84
lines changed

allowed-breaking-changes.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ incompatible-argument:@aws-cdk/aws-apigateway.ProxyResource.addProxy
2323
incompatible-argument:@aws-cdk/aws-apigateway.Resource.addProxy
2424
incompatible-argument:@aws-cdk/aws-apigateway.ResourceBase.addProxy
2525
incompatible-argument:@aws-cdk/aws-apigateway.IResource.addProxy
26+
incompatible-argument:@aws-cdk/aws-servicediscovery.Service.fromServiceAttributes
2627
removed:@aws-cdk/core.ConstructNode.addReference
2728
removed:@aws-cdk/core.ConstructNode.references
2829
removed:@aws-cdk/core.OutgoingReference
29-

packages/@aws-cdk/app-delivery/lib/pipeline-deploy-stack-action.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class PipelineDeployStackAction implements codepipeline.IAction {
9393
/**
9494
* The role used by CloudFormation for the deploy action
9595
*/
96-
private _deploymentRole: iam.IRole;
96+
private _deploymentRole?: iam.IRole;
9797

9898
private readonly stack: cdk.Stack;
9999
private readonly prepareChangeSetAction: cpactions.CloudFormationCreateReplaceChangeSetAction;
@@ -147,6 +147,10 @@ export class PipelineDeployStackAction implements codepipeline.IAction {
147147
}
148148

149149
public get deploymentRole(): iam.IRole {
150+
if (!this._deploymentRole) {
151+
throw new Error(`Use this action in a pipeline first before accessing 'deploymentRole'`);
152+
}
153+
150154
return this._deploymentRole;
151155
}
152156

packages/@aws-cdk/aws-apigateway/lib/restapi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export class RestApi extends Resource implements IRestApi {
202202
* If `deploy` is disabled, you will need to explicitly assign this value in order to
203203
* set up integrations.
204204
*/
205-
public deploymentStage: Stage;
205+
public deploymentStage!: Stage;
206206

207207
/**
208208
* The domain name mapped to this API, if defined through the `domainName`
@@ -242,6 +242,7 @@ export class RestApi extends Resource implements IRestApi {
242242
}
243243

244244
this.root = new RootResource(this, props, resource.attrRootResourceId);
245+
this.restApiRootResourceId = resource.attrRootResourceId;
245246

246247
if (props.domainName) {
247248
this.domainName = this.addDomainName('CustomDomain', props.domainName);

packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import cdk = require('@aws-cdk/core');
22

33
import { CfnVirtualRouter } from './appmesh.generated';
4-
import { IMesh } from './mesh';
4+
import { IMesh, Mesh } from './mesh';
55
import { Route, RouteBaseProps } from './route';
66
import { PortMapping, Protocol } from './shared-interfaces';
77

@@ -119,6 +119,13 @@ export class VirtualRouter extends VirtualRouterBase {
119119
return new ImportedVirtualRouter(scope, id, { meshName, virtualRouterName });
120120
}
121121

122+
/**
123+
* Import an existing virtual router given attributes
124+
*/
125+
public static fromVirtualRouterAttributes(scope: cdk.Construct, id: string, attrs: VirtualRouterAttributes): IVirtualRouter {
126+
return new ImportedVirtualRouter(scope, id, attrs);
127+
}
128+
122129
/**
123130
* The name of the VirtualRouter
124131
*/
@@ -174,7 +181,7 @@ export class VirtualRouter extends VirtualRouterBase {
174181
/**
175182
* Interface with properties ncecessary to import a reusable VirtualRouter
176183
*/
177-
interface VirtualRouterAttributes {
184+
export interface VirtualRouterAttributes {
178185
/**
179186
* The name of the VirtualRouter
180187
*/
@@ -188,6 +195,11 @@ interface VirtualRouterAttributes {
188195
/**
189196
* The AppMesh mesh the VirtualRouter belongs to
190197
*/
198+
readonly mesh?: IMesh;
199+
200+
/**
201+
* The name of the AppMesh mesh the VirtualRouter belongs to
202+
*/
191203
readonly meshName?: string;
192204
}
193205

@@ -205,14 +217,21 @@ class ImportedVirtualRouter extends VirtualRouterBase {
205217
*/
206218
public readonly virtualRouterArn: string;
207219

208-
/**
209-
* The AppMesh mesh the VirtualRouter belongs to
210-
*/
211-
public readonly mesh: IMesh;
220+
private _mesh?: IMesh;
212221

213222
constructor(scope: cdk.Construct, id: string, props: VirtualRouterAttributes) {
214223
super(scope, id);
215224

225+
if (props.mesh) {
226+
this._mesh = props.mesh;
227+
}
228+
if (props.meshName) {
229+
if (props.mesh) {
230+
throw new Error(`Supply either 'mesh' or 'meshName', but not both`);
231+
}
232+
this._mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName);
233+
}
234+
216235
if (props.virtualRouterArn) {
217236
this.virtualRouterArn = props.virtualRouterArn;
218237
this.virtualRouterName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualRouterArn).resourceName!));
@@ -227,4 +246,14 @@ class ImportedVirtualRouter extends VirtualRouterBase {
227246
throw new Error('Need either arn or both names');
228247
}
229248
}
249+
250+
/**
251+
* The AppMesh mesh the VirtualRouter belongs to
252+
*/
253+
public get mesh(): IMesh {
254+
if (!this._mesh) {
255+
throw new Error(`Please supply either 'mesh' or 'meshName' when calling 'fromVirtualRouterAttributes'`);
256+
}
257+
return this._mesh;
258+
}
230259
}

packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,17 @@ export = {
303303
test.done();
304304
},
305305
},
306+
307+
'can import a virtual router'(test: Test) {
308+
// GIVEN
309+
const stack = new cdk.Stack();
310+
311+
// WHEN
312+
const vr = appmesh.VirtualRouter.fromVirtualRouterName(stack, 'Router', 'MyMesh', 'MyRouter');
313+
314+
// THEN
315+
test.ok(vr.mesh !== undefined);
316+
317+
test.done();
318+
},
306319
};

packages/@aws-cdk/aws-codebuild/lib/project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ abstract class ProjectBase extends Resource implements IProject {
175175
* May be unset, in which case this Project is not configured to use a VPC.
176176
* @internal
177177
*/
178-
protected _connections: ec2.Connections;
178+
protected _connections: ec2.Connections | undefined;
179179

180180
/**
181181
* Access the Connections object.

packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export abstract class ApplicationLoadBalancedServiceBase extends cdk.Construct {
233233
/**
234234
* Certificate Manager certificate to associate with the load balancer.
235235
*/
236-
public readonly certificate: ICertificate;
236+
public readonly certificate?: ICertificate;
237237

238238
/**
239239
* The cluster that hosts the service.

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

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,13 @@ export abstract class BaseService extends Resource
221221
}
222222
}
223223

224+
/**
225+
* The CloudMap service created for this service, if any.
226+
*/
227+
public get cloudMapService(): cloudmap.IService | undefined {
228+
return this.cloudmapService;
229+
}
230+
224231
/**
225232
* This method is called to attach this service to an Application Load Balancer.
226233
*
@@ -303,6 +310,68 @@ export abstract class BaseService extends Resource
303310
});
304311
}
305312

313+
/**
314+
* Enable CloudMap service discovery for the service
315+
*
316+
* @returns The created CloudMap service
317+
*/
318+
public enableCloudMap(options: CloudMapOptions): cloudmap.Service {
319+
const sdNamespace = this.cluster.defaultCloudMapNamespace;
320+
if (sdNamespace === undefined) {
321+
throw new Error("Cannot enable service discovery if a Cloudmap Namespace has not been created in the cluster.");
322+
}
323+
324+
// Determine DNS type based on network mode
325+
const networkMode = this.taskDefinition.networkMode;
326+
if (networkMode === NetworkMode.NONE) {
327+
throw new Error("Cannot use a service discovery if NetworkMode is None. Use Bridge, Host or AwsVpc instead.");
328+
}
329+
330+
// Bridge or host network mode requires SRV records
331+
let dnsRecordType = options.dnsRecordType;
332+
333+
if (networkMode === NetworkMode.BRIDGE || networkMode === NetworkMode.HOST) {
334+
if (dnsRecordType === undefined) {
335+
dnsRecordType = cloudmap.DnsRecordType.SRV;
336+
}
337+
if (dnsRecordType !== cloudmap.DnsRecordType.SRV) {
338+
throw new Error("SRV records must be used when network mode is Bridge or Host.");
339+
}
340+
}
341+
342+
// Default DNS record type for AwsVpc network mode is A Records
343+
if (networkMode === NetworkMode.AWS_VPC) {
344+
if (dnsRecordType === undefined) {
345+
dnsRecordType = cloudmap.DnsRecordType.A;
346+
}
347+
}
348+
349+
// If the task definition that your service task specifies uses the AWSVPC network mode and a type SRV DNS record is
350+
// used, you must specify a containerName and containerPort combination
351+
const containerName = dnsRecordType === cloudmap.DnsRecordType.SRV ? this.taskDefinition.defaultContainer!.containerName : undefined;
352+
const containerPort = dnsRecordType === cloudmap.DnsRecordType.SRV ? this.taskDefinition.defaultContainer!.containerPort : undefined;
353+
354+
const cloudmapService = new cloudmap.Service(this, 'CloudmapService', {
355+
namespace: sdNamespace,
356+
name: options.name,
357+
dnsRecordType: dnsRecordType!,
358+
customHealthCheck: { failureThreshold: options.failureThreshold || 1 }
359+
});
360+
361+
const serviceArn = cloudmapService.serviceArn;
362+
363+
// add Cloudmap service to the ECS Service's serviceRegistry
364+
this.addServiceRegistry({
365+
arn: serviceArn,
366+
containerName,
367+
containerPort
368+
});
369+
370+
this.cloudmapService = cloudmapService;
371+
372+
return cloudmapService;
373+
}
374+
306375
/**
307376
* This method returns the specified CloudWatch metric name for this service.
308377
*/
@@ -430,66 +499,6 @@ export abstract class BaseService extends Resource
430499
this.serviceRegistries.push(sr);
431500
}
432501

433-
/**
434-
* Enable CloudMap service discovery for the service
435-
*/
436-
private enableCloudMap(options: CloudMapOptions): cloudmap.Service {
437-
const sdNamespace = this.cluster.defaultCloudMapNamespace;
438-
if (sdNamespace === undefined) {
439-
throw new Error("Cannot enable service discovery if a Cloudmap Namespace has not been created in the cluster.");
440-
}
441-
442-
// Determine DNS type based on network mode
443-
const networkMode = this.taskDefinition.networkMode;
444-
if (networkMode === NetworkMode.NONE) {
445-
throw new Error("Cannot use a service discovery if NetworkMode is None. Use Bridge, Host or AwsVpc instead.");
446-
}
447-
448-
// Bridge or host network mode requires SRV records
449-
let dnsRecordType = options.dnsRecordType;
450-
451-
if (networkMode === NetworkMode.BRIDGE || networkMode === NetworkMode.HOST) {
452-
if (dnsRecordType === undefined) {
453-
dnsRecordType = cloudmap.DnsRecordType.SRV;
454-
}
455-
if (dnsRecordType !== cloudmap.DnsRecordType.SRV) {
456-
throw new Error("SRV records must be used when network mode is Bridge or Host.");
457-
}
458-
}
459-
460-
// Default DNS record type for AwsVpc network mode is A Records
461-
if (networkMode === NetworkMode.AWS_VPC) {
462-
if (dnsRecordType === undefined) {
463-
dnsRecordType = cloudmap.DnsRecordType.A;
464-
}
465-
}
466-
467-
// If the task definition that your service task specifies uses the AWSVPC network mode and a type SRV DNS record is
468-
// used, you must specify a containerName and containerPort combination
469-
const containerName = dnsRecordType === cloudmap.DnsRecordType.SRV ? this.taskDefinition.defaultContainer!.containerName : undefined;
470-
const containerPort = dnsRecordType === cloudmap.DnsRecordType.SRV ? this.taskDefinition.defaultContainer!.containerPort : undefined;
471-
472-
const cloudmapService = new cloudmap.Service(this, 'CloudmapService', {
473-
namespace: sdNamespace,
474-
name: options.name,
475-
dnsRecordType: dnsRecordType!,
476-
customHealthCheck: { failureThreshold: options.failureThreshold || 1 }
477-
});
478-
479-
const serviceArn = cloudmapService.serviceArn;
480-
481-
// add Cloudmap service to the ECS Service's serviceRegistry
482-
this.addServiceRegistry({
483-
arn: serviceArn,
484-
containerName,
485-
containerPort
486-
});
487-
488-
this.cloudmapService = cloudmapService;
489-
490-
return cloudmapService;
491-
}
492-
493502
/**
494503
* Return the default grace period when load balancers are configured and
495504
* healthCheckGracePeriod is not already set

packages/@aws-cdk/aws-ecs/lib/images/ecr.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ export class EcrImage extends ContainerImage {
2121
*/
2222
constructor(private readonly repository: ecr.IRepository, private readonly tag: string) {
2323
super();
24+
25+
this.imageName = this.repository.repositoryUriForTag(this.tag);
2426
}
2527

2628
public bind(_scope: Construct, containerDefinition: ContainerDefinition): ContainerImageConfig {
2729
this.repository.grantPull(containerDefinition.taskDefinition.obtainExecutionRole());
2830

2931
return {
30-
imageName: this.repository.repositoryUriForTag(this.tag)
32+
imageName: this.imageName
3133
};
3234
}
3335
}

packages/@aws-cdk/aws-ecs/lib/log-drivers/generic-log-driver.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ export class GenericLogDriver extends LogDriver {
5151
*
5252
* @param props the generic log driver configuration options.
5353
*/
54-
constructor(private readonly props: GenericLogDriverProps) {
54+
constructor(props: GenericLogDriverProps) {
5555
super();
56+
57+
this.logDriver = props.logDriver;
58+
this.options = props.options || {};
5659
}
5760

5861
/**
5962
* Called when the log driver is configured on a container.
6063
*/
6164
public bind(_scope: Construct, _containerDefinition: ContainerDefinition): LogDriverConfig {
62-
this.logDriver = this.props.logDriver;
63-
this.options = this.props.options || {};
64-
6565
return {
6666
logDriver: this.logDriver,
6767
options: removeEmpty(this.options),

0 commit comments

Comments
 (0)