1
1
import { Construct , IConstruct , IDependable } from "@aws-cdk/cdk" ;
2
- import { subnetName } from './util' ;
2
+ import { DEFAULT_SUBNET_NAME , subnetName } from './util' ;
3
3
import { VpnConnection , VpnConnectionOptions } from './vpn' ;
4
4
5
5
export interface IVpcSubnet extends IConstruct {
@@ -61,9 +61,20 @@ export interface IVpcNetwork extends IConstruct {
61
61
readonly vpnGatewayId ?: string ;
62
62
63
63
/**
64
- * Return the subnets appropriate for the placement strategy
64
+ * Return IDs of the subnets appropriate for the given selection strategy
65
+ *
66
+ * Requires that at least once subnet is matched, throws a descriptive
67
+ * error message otherwise.
68
+ *
69
+ * Prefer to use this method over {@link subnets} if you need to pass subnet
70
+ * IDs to a CloudFormation Resource.
65
71
*/
66
- subnets ( placement ?: VpcPlacementStrategy ) : IVpcSubnet [ ] ;
72
+ subnetIds ( selection ?: SubnetSelection ) : string [ ] ;
73
+
74
+ /**
75
+ * Return a dependable object representing internet connectivity for the given subnets
76
+ */
77
+ subnetInternetDependencies ( selection ?: SubnetSelection ) : IDependable ;
67
78
68
79
/**
69
80
* Return whether the given subnet is one of this VPC's public subnets.
@@ -125,29 +136,29 @@ export enum SubnetType {
125
136
}
126
137
127
138
/**
128
- * Customize how instances are placed inside a VPC
139
+ * Customize subnets that are selected for placement of ENIs
129
140
*
130
141
* Constructs that allow customization of VPC placement use parameters of this
131
142
* type to provide placement settings.
132
143
*
133
144
* By default, the instances are placed in the private subnets.
134
145
*/
135
- export interface VpcPlacementStrategy {
146
+ export interface SubnetSelection {
136
147
/**
137
148
* Place the instances in the subnets of the given type
138
149
*
139
- * At most one of `subnetsToUse ` and `subnetName` can be supplied.
150
+ * At most one of `subnetType ` and `subnetName` can be supplied.
140
151
*
141
152
* @default SubnetType.Private
142
153
*/
143
- subnetsToUse ?: SubnetType ;
154
+ subnetType ?: SubnetType ;
144
155
145
156
/**
146
157
* Place the instances in the subnets with the given name
147
158
*
148
159
* (This is the name supplied in subnetConfiguration).
149
160
*
150
- * At most one of `subnetsToUse ` and `subnetName` can be supplied.
161
+ * At most one of `subnetType ` and `subnetName` can be supplied.
151
162
*
152
163
* @default name
153
164
*/
@@ -200,31 +211,30 @@ export abstract class VpcNetworkBase extends Construct implements IVpcNetwork {
200
211
public readonly natDependencies = new Array < IConstruct > ( ) ;
201
212
202
213
/**
203
- * Return the subnets appropriate for the placement strategy
214
+ * Returns IDs of selected subnets
204
215
*/
205
- public subnets ( placement : VpcPlacementStrategy = { } ) : IVpcSubnet [ ] {
206
- if ( placement . subnetsToUse !== undefined && placement . subnetName !== undefined ) {
207
- throw new Error ( 'At most one of subnetsToUse and subnetName can be supplied' ) ;
208
- }
216
+ public subnetIds ( selection : SubnetSelection = { } ) : string [ ] {
217
+ selection = reifySelectionDefaults ( selection ) ;
209
218
210
- // Select by name
211
- if ( placement . subnetName !== undefined ) {
212
- const allSubnets = this . privateSubnets . concat ( this . publicSubnets ) . concat ( this . isolatedSubnets ) ;
213
- const selectedSubnets = allSubnets . filter ( s => subnetName ( s ) === placement . subnetName ) ;
214
- if ( selectedSubnets . length === 0 ) {
215
- throw new Error ( `No subnets with name: ${ placement . subnetName } ` ) ;
216
- }
217
- return selectedSubnets ;
219
+ const nets = this . subnets ( selection ) ;
220
+ if ( nets . length === 0 ) {
221
+ throw new Error ( `There are no ${ describeSelection ( selection ) } in this VPC. Use a different VPC subnet selection.` ) ;
218
222
}
219
223
220
- // Select by type
221
- if ( placement . subnetsToUse === undefined ) { return this . privateSubnets ; }
224
+ return nets . map ( n => n . subnetId ) ;
225
+ }
222
226
223
- return {
224
- [ SubnetType . Isolated ] : this . isolatedSubnets ,
225
- [ SubnetType . Private ] : this . privateSubnets ,
226
- [ SubnetType . Public ] : this . publicSubnets ,
227
- } [ placement . subnetsToUse ] ;
227
+ /**
228
+ * Return a dependable object representing internet connectivity for the given subnets
229
+ */
230
+ public subnetInternetDependencies ( selection : SubnetSelection = { } ) : IDependable {
231
+ selection = reifySelectionDefaults ( selection ) ;
232
+
233
+ const ret = new CompositeDependable ( ) ;
234
+ for ( const subnet of this . subnets ( selection ) ) {
235
+ ret . add ( subnet . internetConnectivityEstablished ) ;
236
+ }
237
+ return ret ;
228
238
}
229
239
230
240
/**
@@ -260,6 +270,31 @@ export abstract class VpcNetworkBase extends Construct implements IVpcNetwork {
260
270
return this . node . stack . region ;
261
271
}
262
272
273
+ /**
274
+ * Return the subnets appropriate for the placement strategy
275
+ */
276
+ protected subnets ( selection : SubnetSelection = { } ) : IVpcSubnet [ ] {
277
+ selection = reifySelectionDefaults ( selection ) ;
278
+
279
+ // Select by name
280
+ if ( selection . subnetName !== undefined ) {
281
+ const allSubnets = this . privateSubnets . concat ( this . publicSubnets ) . concat ( this . isolatedSubnets ) ;
282
+ const selectedSubnets = allSubnets . filter ( s => subnetName ( s ) === selection . subnetName ) ;
283
+ if ( selectedSubnets . length === 0 ) {
284
+ throw new Error ( `No subnets with name: ${ selection . subnetName } ` ) ;
285
+ }
286
+ return selectedSubnets ;
287
+ }
288
+
289
+ // Select by type
290
+ if ( selection . subnetType === undefined ) { return this . privateSubnets ; }
291
+
292
+ return {
293
+ [ SubnetType . Isolated ] : this . isolatedSubnets ,
294
+ [ SubnetType . Private ] : this . privateSubnets ,
295
+ [ SubnetType . Public ] : this . publicSubnets ,
296
+ } [ selection . subnetType ] ;
297
+ }
263
298
}
264
299
265
300
/**
@@ -335,3 +370,56 @@ export interface VpcSubnetImportProps {
335
370
*/
336
371
subnetId : string ;
337
372
}
373
+
374
+ /**
375
+ * If the placement strategy is completely "default", reify the defaults so
376
+ * consuming code doesn't have to reimplement the same analysis every time.
377
+ *
378
+ * Returns "private subnets" by default.
379
+ */
380
+ function reifySelectionDefaults ( placement : SubnetSelection ) : SubnetSelection {
381
+ if ( placement . subnetType !== undefined && placement . subnetName !== undefined ) {
382
+ throw new Error ( 'Only one of subnetType and subnetName can be supplied' ) ;
383
+ }
384
+
385
+ if ( placement . subnetType === undefined && placement . subnetName === undefined ) {
386
+ return { subnetType : SubnetType . Private } ;
387
+ }
388
+
389
+ return placement ;
390
+ }
391
+
392
+ /**
393
+ * Describe the given placement strategy
394
+ */
395
+ function describeSelection ( placement : SubnetSelection ) : string {
396
+ if ( placement . subnetType !== undefined ) {
397
+ return `'${ DEFAULT_SUBNET_NAME [ placement . subnetType ] } ' subnets` ;
398
+ }
399
+ if ( placement . subnetName !== undefined ) {
400
+ return `subnets named '${ placement . subnetName } '` ;
401
+ }
402
+ return JSON . stringify ( placement ) ;
403
+ }
404
+
405
+ class CompositeDependable implements IDependable {
406
+ private readonly dependables = new Array < IDependable > ( ) ;
407
+
408
+ /**
409
+ * Add a construct to the dependency roots
410
+ */
411
+ public add ( dep : IDependable ) {
412
+ this . dependables . push ( dep ) ;
413
+ }
414
+
415
+ /**
416
+ * Retrieve the current set of dependency roots
417
+ */
418
+ public get dependencyRoots ( ) : IConstruct [ ] {
419
+ const ret = [ ] ;
420
+ for ( const dep of this . dependables ) {
421
+ ret . push ( ...dep . dependencyRoots ) ;
422
+ }
423
+ return ret ;
424
+ }
425
+ }
0 commit comments