@aws-blocks/core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/cdk/blocks-backend.d.ts +4 -1
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +56 -10
- package/dist/cdk/blocks-backend.test.js +71 -1
- package/dist/cdk/blocks-defaults.d.ts +11 -0
- package/dist/cdk/blocks-defaults.d.ts.map +1 -1
- package/dist/cdk/blocks-stack.test.js +23 -3
- package/dist/cdk/compute/compute.d.ts +80 -2
- package/dist/cdk/compute/compute.d.ts.map +1 -1
- package/dist/cdk/compute/compute.js +57 -3
- package/dist/cdk/config-registry.test.js +12 -0
- package/dist/cdk/dashboard-registry.d.ts +41 -0
- package/dist/cdk/dashboard-registry.d.ts.map +1 -0
- package/dist/cdk/dashboard-registry.js +61 -0
- package/dist/cdk/index.d.ts +67 -11
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +100 -18
- package/dist/cdk/internal.d.ts +3 -1
- package/dist/cdk/internal.d.ts.map +1 -1
- package/dist/cdk/internal.js +4 -1
- package/dist/cdk/tracer-registry.d.ts +31 -0
- package/dist/cdk/tracer-registry.d.ts.map +1 -0
- package/dist/cdk/tracer-registry.js +49 -0
- package/dist/cdk/vpc-requirements-registry.d.ts +33 -0
- package/dist/cdk/vpc-requirements-registry.d.ts.map +1 -0
- package/dist/cdk/vpc-requirements-registry.js +46 -0
- package/dist/cdk/vpc-types.d.ts +151 -0
- package/dist/cdk/vpc-types.d.ts.map +1 -0
- package/dist/cdk/vpc-types.js +3 -0
- package/dist/cdk/vpc.d.ts +59 -0
- package/dist/cdk/vpc.d.ts.map +1 -0
- package/dist/cdk/vpc.js +298 -0
- package/dist/cdk/vpc.test.d.ts +2 -0
- package/dist/cdk/vpc.test.d.ts.map +1 -0
- package/dist/cdk/vpc.test.js +285 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -0
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +2 -0
- package/dist/hosting.test.js +39 -1
- package/dist/index.cdk.d.ts +2 -1
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +1 -1
- package/dist/lambda-handler.js +9 -2
- package/dist/lambda-handler.test.js +61 -1
- package/dist/raw-route.d.ts +15 -1
- package/dist/raw-route.d.ts.map +1 -1
- package/dist/raw-route.js +96 -12
- package/dist/raw-route.test.js +332 -1
- package/dist/scripts/dev-server.d.ts.map +1 -1
- package/dist/scripts/dev-server.js +11 -0
- package/dist/scripts/extract-ts-types.d.ts.map +1 -1
- package/dist/scripts/extract-ts-types.js +107 -23
- package/dist/scripts/extract-ts-types.test.js +225 -26
- package/dist/scripts/generate-spec.d.ts.map +1 -1
- package/dist/scripts/generate-spec.js +14 -5
- package/dist/scripts/generate-spec.test.js +93 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -1
- package/src/cdk/blocks-backend.test.ts +144 -60
- package/src/cdk/blocks-backend.ts +298 -239
- package/src/cdk/blocks-defaults.ts +12 -0
- package/src/cdk/blocks-stack.test.ts +32 -13
- package/src/cdk/compute/compute.ts +105 -3
- package/src/cdk/config-registry.test.ts +14 -0
- package/src/cdk/dashboard-registry.ts +68 -0
- package/src/cdk/index.ts +426 -298
- package/src/cdk/internal.ts +6 -2
- package/src/cdk/tracer-registry.ts +54 -0
- package/src/cdk/vpc-requirements-registry.ts +63 -0
- package/src/cdk/vpc-types.ts +158 -0
- package/src/cdk/vpc.test.ts +348 -0
- package/src/cdk/vpc.ts +336 -0
- package/src/errors.ts +5 -0
- package/src/hosting.test.ts +59 -1
- package/src/hosting.ts +3 -0
- package/src/index.cdk.ts +7 -0
- package/src/lambda-handler.test.ts +79 -1
- package/src/lambda-handler.ts +11 -2
- package/src/raw-route.test.ts +427 -1
- package/src/raw-route.ts +125 -12
- package/src/scripts/dev-server.ts +12 -1
- package/src/scripts/extract-ts-types.test.ts +228 -26
- package/src/scripts/extract-ts-types.ts +104 -20
- package/src/scripts/generate-spec.test.ts +101 -0
- package/src/scripts/generate-spec.ts +15 -5
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,18 @@ The body is JSON-RPC 2.0:
|
|
|
76
76
|
- `params` is a POSITIONAL array of the method's arguments. A named object also works (its values are used in order).
|
|
77
77
|
- Errors come back as HTTP `200` with an `error` object in the body (per JSON-RPC), not as a non-2xx status.
|
|
78
78
|
|
|
79
|
+
Error responses use these JSON-RPC codes:
|
|
80
|
+
|
|
81
|
+
| Code | Meaning | When Blocks uses it |
|
|
82
|
+
|------|---------|---------------------|
|
|
83
|
+
| `-32700` | Parse error | Request body is not valid JSON. |
|
|
84
|
+
| `-32600` | Invalid Request | Body is not a valid Blocks JSON-RPC request, such as missing `jsonrpc: "2.0"` or a `method` that is not `namespace.method`. |
|
|
85
|
+
| `-32601` | Method not found | The requested namespace or method is not exported by the backend. |
|
|
86
|
+
| `-32602` | Invalid params | Reserved for JSON-RPC parameter validation failures. Application-level validation should throw an `ApiError` with the appropriate HTTP status instead. |
|
|
87
|
+
| `-32603` | Internal error | Reserved for generic JSON-RPC internal errors. Unhandled application errors currently use code `500`. |
|
|
88
|
+
|
|
89
|
+
Errors thrown with `ApiError` use the positive HTTP status as the JSON-RPC `error.code` (for example, `401`, `404`, or `409`) and include `error.data.name` when an error name is available. The typed client converts JSON-RPC errors back into `ApiError`.
|
|
90
|
+
|
|
79
91
|
Working example:
|
|
80
92
|
|
|
81
93
|
```bash
|
|
@@ -88,7 +88,9 @@ export declare class BlocksBackend extends Construct {
|
|
|
88
88
|
get gateway(): apigateway.RestApi;
|
|
89
89
|
/** The default compute's RPC endpoint URL. To be removed once consumers move to the multi-compute model. */
|
|
90
90
|
get apiUrl(): string;
|
|
91
|
-
/** The default compute's handler CloudWatch log group.
|
|
91
|
+
/** The default compute's handler CloudWatch log group. Its retention comes from
|
|
92
|
+
* the compute's `logRetention` (falling back to `defaults.logRetention`); the
|
|
93
|
+
* `bb-logger` CDK construct is a no-op and no longer touches it. */
|
|
92
94
|
get handlerLogGroup(): cdk.aws_logs.ILogGroup;
|
|
93
95
|
private requireDefaultCompute;
|
|
94
96
|
/**
|
|
@@ -111,6 +113,7 @@ export declare class BlocksBackend extends Construct {
|
|
|
111
113
|
* fallback to the (token-free) construct id should no resolvable name exist.
|
|
112
114
|
*/
|
|
113
115
|
get fullId(): string;
|
|
116
|
+
private _vpcOptions?;
|
|
114
117
|
private constructor();
|
|
115
118
|
static create(scope: Construct, id: string, props: CoreBlocksBackendProps): Promise<BlocksBackend>;
|
|
116
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks-backend.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"blocks-backend.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AACnC,OAAO,KAAK,KAAK,UAAU,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAuB,MAAM,sCAAsC,CAAC;AAQvG;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAkB/C;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAU,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,cAAc,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IACjE,8EAA8E;IAC9E,qBAAqB,EAAE,qBAAqB,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,MAAM;;EAqGxF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAC3C,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAC3C;;;;OAIG;IACH,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAC1C,yFAAyF;IACzF,SAAgB,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC;IACzC,8EAA8E;IAC9E,SAAgB,QAAQ,EAAE,cAAc,CAAC;IACzC,iGAAiG;IACjG,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,2GAA2G;IAC3G,IAAI,OAAO,IAAI,GAAG,CAAC,iBAAiB,CAAC,cAAc,CAElD;IACD,gHAAgH;IAChH,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAEhC;IACD,4GAA4G;IAC5G,IAAI,MAAM,IAAI,MAAM,CAEnB;IACD;;wEAEoE;IACpE,IAAI,eAAe,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAE5C;IAED,OAAO,CAAC,qBAAqB;IAS7B;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,MAAM,IAAI,MAAM,CAUnB;IAED,OAAO,CAAC,WAAW,CAAC,CAAmB;IAEvC,OAAO;WA6BM,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB;CAyD/E"}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import * as cdk from 'aws-cdk-lib';
|
|
4
5
|
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
5
6
|
import { CfnGroup } from 'aws-cdk-lib/aws-resourcegroups';
|
|
6
7
|
import { Construct } from 'constructs';
|
|
7
|
-
import { pathToFileURL } from 'node:url';
|
|
8
|
-
import { addBlocksStackMetadata } from './stack-metadata.js';
|
|
9
|
-
import { finalizeConfigRegistry, registerConfig } from './config-registry.js';
|
|
10
8
|
import { registerBuiltinRoutes } from '../builtin-routes.js';
|
|
11
9
|
import { getComputes } from './compute/compute-registry.js';
|
|
10
|
+
import { finalizeConfigRegistry, registerConfig } from './config-registry.js';
|
|
11
|
+
import { finalizeDashboards } from './dashboard-registry.js';
|
|
12
|
+
import { finalizeTracing } from './tracer-registry.js';
|
|
13
|
+
import { addBlocksStackMetadata } from './stack-metadata.js';
|
|
14
|
+
import { anyRequirementNeedsVpc, finalizeVpc, getOrCreateVpc, initializeVpc } from './vpc.js';
|
|
12
15
|
/**
|
|
13
16
|
* Validate that the Node.js process was started with `--conditions=cdk`.
|
|
14
17
|
*
|
|
@@ -20,7 +23,7 @@ import { getComputes } from './compute/compute-registry.js';
|
|
|
20
23
|
export function assertCdkConditionActive() {
|
|
21
24
|
const nodeOptions = process.env.NODE_OPTIONS ?? '';
|
|
22
25
|
const execArgv = process.execArgv ?? [];
|
|
23
|
-
const hasCdkCondition = execArgv.some(arg => arg === '--conditions=cdk') ||
|
|
26
|
+
const hasCdkCondition = execArgv.some((arg) => arg === '--conditions=cdk') ||
|
|
24
27
|
execArgv.some((arg, i) => (arg === '--conditions' || arg === '-C') && execArgv[i + 1] === 'cdk') ||
|
|
25
28
|
nodeOptions.includes('--conditions=cdk') ||
|
|
26
29
|
/(?:--conditions|-C)\s+cdk/.test(nodeOptions);
|
|
@@ -72,13 +75,17 @@ export function setupBlocksInfra(scope, props, id) {
|
|
|
72
75
|
assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('lambda.amazonaws.com')),
|
|
73
76
|
managedPolicies: [
|
|
74
77
|
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
|
|
78
|
+
// When Lambda is placed in a VPC it needs ENI management permissions
|
|
79
|
+
...(props.defaults.vpc
|
|
80
|
+
? [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole')]
|
|
81
|
+
: []),
|
|
75
82
|
],
|
|
76
83
|
});
|
|
77
84
|
// ── Resource Groups ───────────────────────────────────────────────────
|
|
78
85
|
let rootStack = cdk.Stack.of(scope);
|
|
79
86
|
while (rootStack.nestedStackParent)
|
|
80
87
|
rootStack = rootStack.nestedStackParent;
|
|
81
|
-
const groupPrefix =
|
|
88
|
+
const groupPrefix = id && id !== rootStack.stackName ? `${rootStack.stackName}-${id}` : rootStack.stackName;
|
|
82
89
|
new CfnGroup(scope, 'StackResources', {
|
|
83
90
|
name: `${groupPrefix}-resources`,
|
|
84
91
|
resourceQuery: {
|
|
@@ -111,12 +118,20 @@ export function setupBlocksInfra(scope, props, id) {
|
|
|
111
118
|
// ── Console redirect routes ───────────────────────────────────────────
|
|
112
119
|
const region = cdk.Fn.ref('AWS::Region');
|
|
113
120
|
const resourcesUrl = cdk.Fn.join('', [
|
|
114
|
-
'https://',
|
|
115
|
-
|
|
121
|
+
'https://',
|
|
122
|
+
region,
|
|
123
|
+
'.console.aws.amazon.com/resource-groups/group/',
|
|
124
|
+
`${groupPrefix}-resources`,
|
|
125
|
+
'?region=',
|
|
126
|
+
region,
|
|
116
127
|
]);
|
|
117
128
|
const settingsUrl = cdk.Fn.join('', [
|
|
118
|
-
'https://',
|
|
119
|
-
|
|
129
|
+
'https://',
|
|
130
|
+
region,
|
|
131
|
+
'.console.aws.amazon.com/resource-groups/group/',
|
|
132
|
+
`${groupPrefix}-settings`,
|
|
133
|
+
'?region=',
|
|
134
|
+
region,
|
|
120
135
|
]);
|
|
121
136
|
registerConfig(scope, 'BB_RESOURCES_GROUP_URL', resourcesUrl);
|
|
122
137
|
registerConfig(scope, 'BB_SETTINGS_GROUP_URL', settingsUrl);
|
|
@@ -166,7 +181,9 @@ export class BlocksBackend extends Construct {
|
|
|
166
181
|
get apiUrl() {
|
|
167
182
|
return this.requireDefaultCompute().apiUrl;
|
|
168
183
|
}
|
|
169
|
-
/** The default compute's handler CloudWatch log group.
|
|
184
|
+
/** The default compute's handler CloudWatch log group. Its retention comes from
|
|
185
|
+
* the compute's `logRetention` (falling back to `defaults.logRetention`); the
|
|
186
|
+
* `bb-logger` CDK construct is a no-op and no longer touches it. */
|
|
170
187
|
get handlerLogGroup() {
|
|
171
188
|
return this.requireDefaultCompute().logGroup;
|
|
172
189
|
}
|
|
@@ -206,16 +223,25 @@ export class BlocksBackend extends Construct {
|
|
|
206
223
|
}
|
|
207
224
|
return `${stackName}-${this.node.id}`;
|
|
208
225
|
}
|
|
226
|
+
_vpcOptions;
|
|
209
227
|
constructor(scope, id, props) {
|
|
210
228
|
super(scope, id);
|
|
211
229
|
this.backendHandlerPath = props.backendHandlerPath;
|
|
212
230
|
this.backendModulePath = props.backendCDKPath;
|
|
231
|
+
this._vpcOptions = props.defaults.vpc;
|
|
213
232
|
// Expose self to Building Blocks at CDK time
|
|
214
233
|
globalThis.CURRENT_BLOCKS_STACK = this;
|
|
215
234
|
// Store defaults on the backend (not the stack) so several BlocksBackends
|
|
216
235
|
// in one stack each keep their own posture; Building Blocks resolve them by
|
|
217
236
|
// walking up to their owning backend (see Scope.defaults).
|
|
218
237
|
this.defaults = props.defaults;
|
|
238
|
+
// Initialize VPC context before the default compute is created and before
|
|
239
|
+
// BBs are constructed, so both can discover it: the default compute
|
|
240
|
+
// (LambdaCompute) reads it via getVpcContext(this) to place its function in
|
|
241
|
+
// the VPC, and BBs (e.g. bb-data) read it to co-locate their resources.
|
|
242
|
+
if (this._vpcOptions) {
|
|
243
|
+
initializeVpc(this, this._vpcOptions);
|
|
244
|
+
}
|
|
219
245
|
const infra = setupBlocksInfra(this, props, id);
|
|
220
246
|
this.executionRole = infra.executionRole;
|
|
221
247
|
// The default compute (and thus handler/gateway) is created in create(),
|
|
@@ -247,6 +273,26 @@ export class BlocksBackend extends Construct {
|
|
|
247
273
|
addBlocksStackMetadata(cdk.Stack.of(backend));
|
|
248
274
|
// Finalize BB config → S3 (after all BBs have registered their config)
|
|
249
275
|
finalizeConfigRegistry(backend, backend.executionRole, getComputes(backend));
|
|
276
|
+
// Tracing is presence-gated: if the app contains a Tracer, enable X-Ray on
|
|
277
|
+
// every compute. Runs before the dashboard finalize.
|
|
278
|
+
finalizeTracing(backend, backend.executionRole);
|
|
279
|
+
// Build any deferred Dashboards now that every compute's observability
|
|
280
|
+
// state is settled — so the dashboard is order-independent.
|
|
281
|
+
finalizeDashboards(backend);
|
|
282
|
+
// Finalize VPC. Derived resource: use the customer's if provided, else
|
|
283
|
+
// lazily create one only if a Building Block requires it.
|
|
284
|
+
if (backend._vpcOptions) {
|
|
285
|
+
finalizeVpc(backend, backend._vpcOptions);
|
|
286
|
+
}
|
|
287
|
+
else if (anyRequirementNeedsVpc(backend)) {
|
|
288
|
+
const derived = getOrCreateVpc(backend);
|
|
289
|
+
const options = { network: derived };
|
|
290
|
+
initializeVpc(backend, options);
|
|
291
|
+
finalizeVpc(backend, options);
|
|
292
|
+
cdk.Annotations.of(backend).addInfoV2('blocks:vpc:derived', 'A Building Block required a VPC and none was provided, so Blocks created one ' +
|
|
293
|
+
'(with a NAT gateway, which has an ongoing cost). Pass `defaults.vpc: { network }` to ' +
|
|
294
|
+
'bring your own. See packages/blocks/VPC.md.');
|
|
295
|
+
}
|
|
250
296
|
return backend;
|
|
251
297
|
}
|
|
252
298
|
}
|
|
@@ -14,6 +14,7 @@ import { BlocksBackend } from './blocks-backend.js';
|
|
|
14
14
|
import { BlocksPresets } from './blocks-defaults.js';
|
|
15
15
|
import { Compute } from './compute/compute.js';
|
|
16
16
|
import { Scope } from './index.js';
|
|
17
|
+
import { getVpcContext } from './vpc.js';
|
|
17
18
|
// A real app gets its default compute from @aws-blocks/bb-lambda-compute (via
|
|
18
19
|
// @aws-blocks/blocks), which core's own tests can't depend on. Use an
|
|
19
20
|
// equivalent inline stub: a Compute that owns a NodejsFunction + API Gateway,
|
|
@@ -31,6 +32,11 @@ class StubLambdaCompute extends Compute {
|
|
|
31
32
|
this.logGroup = new cdk.aws_logs.LogGroup(this, 'HandlerLogGroup', {
|
|
32
33
|
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
33
34
|
});
|
|
35
|
+
// Discover the shared VPC context from the owning stack/backend, mirroring
|
|
36
|
+
// LambdaCompute — so the core-side VPC placement plumbing (initializeVpc →
|
|
37
|
+
// getVpcContext) is exercised by these tests without depending on
|
|
38
|
+
// @aws-blocks/bb-lambda-compute.
|
|
39
|
+
const vpcContext = getVpcContext(this);
|
|
34
40
|
this.fn = new lambda.NodejsFunction(this, 'Handler', {
|
|
35
41
|
entry: this.backendHandlerPath,
|
|
36
42
|
runtime: cdk.aws_lambda.Runtime.NODEJS_22_X,
|
|
@@ -39,6 +45,13 @@ class StubLambdaCompute extends Compute {
|
|
|
39
45
|
logGroup: this.logGroup,
|
|
40
46
|
environment: { BLOCKS_STACK_NAME: this.backendStackName },
|
|
41
47
|
bundling: { minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } },
|
|
48
|
+
...(vpcContext
|
|
49
|
+
? {
|
|
50
|
+
vpc: vpcContext.vpc,
|
|
51
|
+
vpcSubnets: vpcContext.computeSubnets,
|
|
52
|
+
securityGroups: [vpcContext.computeSecurityGroup],
|
|
53
|
+
}
|
|
54
|
+
: {}),
|
|
42
55
|
});
|
|
43
56
|
this.apiGateway = new apigateway.RestApi(this, 'API', { restApiName: 'Blocks API' });
|
|
44
57
|
this.apiGateway.root.addProxy({
|
|
@@ -50,6 +63,16 @@ class StubLambdaCompute extends Compute {
|
|
|
50
63
|
setEnv(key, value) {
|
|
51
64
|
this.fn.addEnvironment(key, value);
|
|
52
65
|
}
|
|
66
|
+
applyTracing() { }
|
|
67
|
+
healthWidgets(_region) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
loggingWidgets(_region) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
tracingWidgets(_region) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
53
76
|
}
|
|
54
77
|
const stubComputeFactory = (root) => new StubLambdaCompute(root, 'DefaultCompute');
|
|
55
78
|
// Simulate the CDK condition being active (tests import CDK files directly)
|
|
@@ -65,7 +88,12 @@ const EXECUTION_ROLE_MARKER_ACTION = 'blocks-test:MarkerAction';
|
|
|
65
88
|
const importMetaHandlerPath = join(__dirname, '__fixtures__', 'import-meta-handler.js');
|
|
66
89
|
// Wraps BlocksBackend.create, injecting the stub default-compute factory the way
|
|
67
90
|
// @aws-blocks/blocks injects LambdaCompute — so tests don't repeat it 15 times.
|
|
68
|
-
const makeBackend = (scope, id, backendCDKPath) => BlocksBackend.create(scope, id, {
|
|
91
|
+
const makeBackend = (scope, id, backendCDKPath) => BlocksBackend.create(scope, id, {
|
|
92
|
+
backendHandlerPath: handlerPath,
|
|
93
|
+
backendCDKPath,
|
|
94
|
+
defaults: BlocksPresets.production,
|
|
95
|
+
defaultComputeFactory: stubComputeFactory,
|
|
96
|
+
});
|
|
69
97
|
describe('ESM cache-busting (multi-stage)', () => {
|
|
70
98
|
test('BlocksBackend.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
|
|
71
99
|
const app = new cdk.App();
|
|
@@ -294,3 +322,45 @@ describe('infrastructure defaults (backend-anchored)', () => {
|
|
|
294
322
|
assert.strictEqual(inner.defaults, BlocksPresets.sandbox);
|
|
295
323
|
});
|
|
296
324
|
});
|
|
325
|
+
describe('VPC placement', () => {
|
|
326
|
+
test('places the handler Lambda in the VPC with a security group when vpc is provided', async () => {
|
|
327
|
+
const app = new cdk.App();
|
|
328
|
+
const parent = new cdk.Stack(app, 'VpcParent', {
|
|
329
|
+
env: { account: '123456789012', region: 'us-east-1' },
|
|
330
|
+
});
|
|
331
|
+
const vpc = new cdk.aws_ec2.Vpc(parent, 'AppVpc', { maxAzs: 2, natGateways: 1 });
|
|
332
|
+
await BlocksBackend.create(parent, 'Blocks', {
|
|
333
|
+
backendHandlerPath: handlerPath,
|
|
334
|
+
backendCDKPath: sideEffectBackendPath,
|
|
335
|
+
defaults: { ...BlocksPresets.production, vpc: { network: vpc } },
|
|
336
|
+
defaultComputeFactory: stubComputeFactory,
|
|
337
|
+
});
|
|
338
|
+
const template = Template.fromStack(parent);
|
|
339
|
+
// The handler Lambda is VPC-attached: it has a VpcConfig with subnets + SGs.
|
|
340
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
341
|
+
VpcConfig: Match.objectLike({
|
|
342
|
+
SubnetIds: Match.anyValue(),
|
|
343
|
+
SecurityGroupIds: Match.anyValue(),
|
|
344
|
+
}),
|
|
345
|
+
});
|
|
346
|
+
// The VPC-access managed policy is attached to the execution role.
|
|
347
|
+
const policies = template.findResources('AWS::IAM::Role');
|
|
348
|
+
const hasVpcManagedPolicy = Object.values(policies).some((role) => JSON.stringify(role.Properties?.ManagedPolicyArns ?? []).includes('AWSLambdaVPCAccessExecutionRole'));
|
|
349
|
+
assert.ok(hasVpcManagedPolicy, 'execution role should have the VPC access managed policy');
|
|
350
|
+
});
|
|
351
|
+
test('no VpcConfig on the handler when vpc is omitted', async () => {
|
|
352
|
+
const app = new cdk.App();
|
|
353
|
+
const parent = new cdk.Stack(app, 'NoVpcParent');
|
|
354
|
+
await BlocksBackend.create(parent, 'Blocks', {
|
|
355
|
+
backendHandlerPath: handlerPath,
|
|
356
|
+
backendCDKPath: sideEffectBackendPath,
|
|
357
|
+
defaults: BlocksPresets.production,
|
|
358
|
+
defaultComputeFactory: stubComputeFactory,
|
|
359
|
+
});
|
|
360
|
+
const template = Template.fromStack(parent);
|
|
361
|
+
const fns = template.findResources('AWS::Lambda::Function');
|
|
362
|
+
for (const fn of Object.values(fns)) {
|
|
363
|
+
assert.strictEqual(fn.Properties?.VpcConfig, undefined, 'handler must not have VpcConfig when no VPC is configured');
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
2
2
|
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
3
|
+
import type { BlocksVpcOptions } from './vpc-types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Request-rate limits applied to an API Gateway stage. On a REST API these are
|
|
5
6
|
* requests/second; on a WebSocket API the unit is messages/second across the
|
|
@@ -89,6 +90,16 @@ export interface BlocksDefaults {
|
|
|
89
90
|
* `ensureApiGatewayAccount` for the full multi-stack teardown caveat.
|
|
90
91
|
*/
|
|
91
92
|
accessLogging: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Place the app's compute and VPC-resident resources in a VPC. Pass a
|
|
95
|
+
* standard CDK VPC — Blocks handles Lambda placement, endpoint provisioning
|
|
96
|
+
* (based on which Building Blocks are in scope), and security-group wiring.
|
|
97
|
+
*
|
|
98
|
+
* Omit for no VPC (the preset default — Lambda runs in the AWS-managed
|
|
99
|
+
* network). A Building Block that can't function without a VPC causes Blocks
|
|
100
|
+
* to derive one on demand even when this is unset.
|
|
101
|
+
*/
|
|
102
|
+
vpc?: BlocksVpcOptions;
|
|
92
103
|
}
|
|
93
104
|
/**
|
|
94
105
|
* Prepared starting points for {@link BlocksDefaults}. Pick one and override
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks-defaults.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-defaults.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"blocks-defaults.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-defaults.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;OAKG;IACH,aAAa,EAAE,aAAa,CAAC;IAE7B;;;;OAIG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAE5B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,OAAO,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAEzD;;;;;;;OAOG;IACH,YAAY,EAAE,aAAa,CAAC;IAE5B;;;;;;;OAOG;IACH,UAAU,EAAE,gBAAgB,CAAC;IAE7B;;;;;;;;;;;;;;OAcG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,gBAAgB,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;IACzB,qFAAqF;;;;;;;;;;;;;IAUrF;;;;;OAKG;;;;;;;;;;;;;CAcsC,CAAC"}
|
|
@@ -11,7 +11,7 @@ import { BLOCKS_RPC_PREFIX } from '../constants.js';
|
|
|
11
11
|
import { BlocksBackend } from './blocks-backend.js';
|
|
12
12
|
import { Compute } from './compute/compute.js';
|
|
13
13
|
import { getComputes } from './compute/compute-registry.js';
|
|
14
|
-
import {
|
|
14
|
+
import { BlocksPresets, BlocksStack, Scope } from './index.js';
|
|
15
15
|
// A real app gets its default compute from @aws-blocks/bb-lambda-compute (via
|
|
16
16
|
// @aws-blocks/blocks), which core's own tests can't depend on. Use an
|
|
17
17
|
// equivalent inline stub: a Compute that owns a NodejsFunction + API Gateway,
|
|
@@ -48,6 +48,16 @@ class StubLambdaCompute extends Compute {
|
|
|
48
48
|
setEnv(key, value) {
|
|
49
49
|
this.fn.addEnvironment(key, value);
|
|
50
50
|
}
|
|
51
|
+
applyTracing() { }
|
|
52
|
+
healthWidgets(_region) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
loggingWidgets(_region) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
tracingWidgets(_region) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
const stubComputeFactory = (root) => new StubLambdaCompute(root, 'DefaultCompute');
|
|
53
63
|
// Simulate the CDK condition being active (tests import CDK files directly)
|
|
@@ -60,8 +70,18 @@ const sideEffectBackendPath = join(__dirname, '__fixtures__', 'side-effect-backe
|
|
|
60
70
|
const factoryBackendPath = join(__dirname, '__fixtures__', 'factory-backend.js');
|
|
61
71
|
// Wrap create(), injecting the stub default-compute factory the way
|
|
62
72
|
// @aws-blocks/blocks injects LambdaCompute — so tests don't repeat it.
|
|
63
|
-
const makeStack = (scope, id, backendCDKPath) => BlocksStack.create(scope, id, {
|
|
64
|
-
|
|
73
|
+
const makeStack = (scope, id, backendCDKPath) => BlocksStack.create(scope, id, {
|
|
74
|
+
backendHandlerPath: handlerPath,
|
|
75
|
+
backendCDKPath,
|
|
76
|
+
defaults: BlocksPresets.production,
|
|
77
|
+
defaultComputeFactory: stubComputeFactory,
|
|
78
|
+
});
|
|
79
|
+
const makeBackend = (scope, id, backendCDKPath) => BlocksBackend.create(scope, id, {
|
|
80
|
+
backendHandlerPath: handlerPath,
|
|
81
|
+
backendCDKPath,
|
|
82
|
+
defaults: BlocksPresets.production,
|
|
83
|
+
defaultComputeFactory: stubComputeFactory,
|
|
84
|
+
});
|
|
65
85
|
describe('ESM cache-busting (multi-stage)', () => {
|
|
66
86
|
test('BlocksStack.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
|
|
67
87
|
const app = new cdk.App();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IWidget } from 'aws-cdk-lib/aws-cloudwatch';
|
|
1
2
|
import type { ScopeOptions } from '../../common/index.js';
|
|
2
3
|
import { Scope } from '../index.js';
|
|
3
4
|
/**
|
|
@@ -11,11 +12,17 @@ import { Scope } from '../index.js';
|
|
|
11
12
|
* compute in an app runs the same backend and agrees on the resource-name
|
|
12
13
|
* namespace.
|
|
13
14
|
*
|
|
15
|
+
* **Observability is compute-owned at deploy time.** Logging is always on (a
|
|
16
|
+
* compute owns its log group and captures stdout; retention is a compute-level
|
|
17
|
+
* setting), so there is no "enable logging" — logs always exist. Tracing, by
|
|
18
|
+
* contrast, provisions real infrastructure (X-Ray) and has cost, so it is
|
|
19
|
+
* enabled explicitly via {@link enableTracing} — which the framework calls on
|
|
20
|
+
* every compute when the app contains a `Tracer` (presence-gated).
|
|
21
|
+
*
|
|
14
22
|
* The abstract base lives in core (a framework primitive); concrete computes
|
|
15
23
|
* live in their own packages (e.g. `LambdaCompute` in `@aws-blocks/bb-lambda-compute`).
|
|
16
24
|
*
|
|
17
|
-
* @internal Not exported from the package's public entry points.
|
|
18
|
-
* cannot instantiate a compute until the customer-facing surface exists.
|
|
25
|
+
* @internal Not exported from the package's public entry points.
|
|
19
26
|
*/
|
|
20
27
|
export declare abstract class Compute extends Scope {
|
|
21
28
|
/**
|
|
@@ -24,6 +31,16 @@ export declare abstract class Compute extends Scope {
|
|
|
24
31
|
* unpopulated (no compute assignment surface yet).
|
|
25
32
|
*/
|
|
26
33
|
readonly namespaces: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Whether tracing has been enabled on this compute — flipped by
|
|
36
|
+
* {@link enableTracing}. Private so it can't be set independently of the
|
|
37
|
+
* infra; read internally by {@link dashboardSection} to decide whether to
|
|
38
|
+
* render the traces section.
|
|
39
|
+
*/
|
|
40
|
+
private tracerEnabled;
|
|
41
|
+
/** Read-only view of {@link tracerEnabled} for subclasses (e.g. to guard
|
|
42
|
+
* their `tracingWidgets` builder). Subclasses can read but not set it. */
|
|
43
|
+
protected get isTracerEnabled(): boolean;
|
|
27
44
|
constructor(id: string, options?: ScopeOptions);
|
|
28
45
|
/**
|
|
29
46
|
* Inject a runtime configuration value (an environment variable) into this
|
|
@@ -31,5 +48,66 @@ export declare abstract class Compute extends Scope {
|
|
|
31
48
|
* directly so config targets the right compute.
|
|
32
49
|
*/
|
|
33
50
|
abstract setEnv(key: string, value: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Enable distributed tracing on this compute: mark it traced (so the Dashboard
|
|
53
|
+
* renders its traces section) and turn on the compute's active tracing via
|
|
54
|
+
* {@link applyTracing}. Idempotent — the framework calls this on **every**
|
|
55
|
+
* compute when the app contains a `Tracer` (tracing is presence-gated, not
|
|
56
|
+
* per-compute), so calling it more than once is a no-op.
|
|
57
|
+
*/
|
|
58
|
+
enableTracing(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Turn on this compute's active tracing (e.g. X-Ray) and grant its role the
|
|
61
|
+
* permission to publish trace segments. Called by {@link enableTracing};
|
|
62
|
+
* `protected` so tracing can't be turned on without marking the compute
|
|
63
|
+
* traced.
|
|
64
|
+
*/
|
|
65
|
+
protected abstract applyTracing(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Build this compute's CloudWatch Dashboard section: health widgets and log
|
|
68
|
+
* widgets **always** (logs are always captured for a compute), plus trace
|
|
69
|
+
* widgets **only when** tracing is enabled on this compute (via
|
|
70
|
+
* {@link enableTracing}).
|
|
71
|
+
*
|
|
72
|
+
* This is the single public entry the Dashboard Building Block uses; the
|
|
73
|
+
* per-kind builders below are `protected`. Whether the logs / traces sections
|
|
74
|
+
* are actually shown is a display choice the Dashboard makes on top (its
|
|
75
|
+
* `logs` / `traces` options) — this returns what the compute *has*.
|
|
76
|
+
*
|
|
77
|
+
* @param region - AWS region the widgets query metrics in.
|
|
78
|
+
*/
|
|
79
|
+
dashboardSection(region: string): ComputeDashboardSection;
|
|
80
|
+
/**
|
|
81
|
+
* Build this compute's health widget rows. Implemented by a concrete compute;
|
|
82
|
+
* obtained only via {@link dashboardSection}.
|
|
83
|
+
*/
|
|
84
|
+
protected abstract healthWidgets(region: string): IWidget[][];
|
|
85
|
+
/**
|
|
86
|
+
* Build this compute's **log** widget rows for its own log group. Logs always
|
|
87
|
+
* exist, so this is always available; the Dashboard's `logs` option decides
|
|
88
|
+
* whether to render it.
|
|
89
|
+
*/
|
|
90
|
+
protected abstract loggingWidgets(region: string): IWidget[][];
|
|
91
|
+
/**
|
|
92
|
+
* Build this compute's **trace** widget rows. Gated behind
|
|
93
|
+
* {@link dashboardSection} so it is only used when tracing is enabled.
|
|
94
|
+
*/
|
|
95
|
+
protected abstract tracingWidgets(region: string): IWidget[][];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A compute's self-reported CloudWatch Dashboard section. `health` and `logging`
|
|
99
|
+
* are always present; `tracing` is populated only when tracing is enabled on the
|
|
100
|
+
* compute. (The Dashboard may still hide `logging` / `tracing` via its display
|
|
101
|
+
* options.)
|
|
102
|
+
*/
|
|
103
|
+
export interface ComputeDashboardSection {
|
|
104
|
+
/** Display label used as the compute's group header. */
|
|
105
|
+
label: string;
|
|
106
|
+
/** Health widget rows — always present. */
|
|
107
|
+
health: IWidget[][];
|
|
108
|
+
/** Log widget rows — always present (logs are always captured). */
|
|
109
|
+
logging?: IWidget[][];
|
|
110
|
+
/** Trace widget rows — present only when tracing is enabled. */
|
|
111
|
+
tracing?: IWidget[][];
|
|
34
112
|
}
|
|
35
113
|
//# sourceMappingURL=compute.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compute.d.ts","sourceRoot":"","sources":["../../../src/cdk/compute/compute.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC
|
|
1
|
+
{"version":3,"file":"compute.d.ts","sourceRoot":"","sources":["../../../src/cdk/compute/compute.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,8BAAsB,OAAQ,SAAQ,KAAK;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAM;IAEnC;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAS;IAE9B;8EAC0E;IAC1E,SAAS,KAAK,eAAe,IAAI,OAAO,CAEvC;gBAEW,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;IAQ9C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAEjD;;;;;;OAMG;IACH,aAAa,IAAI,IAAI;IAMrB;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,IAAI;IAEvC;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,uBAAuB;IAWzD;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,EAAE;IAE7D;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,EAAE;IAE9D;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,EAAE;CAC9D;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;IACpB,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;IACtB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;CACtB"}
|
|
@@ -13,11 +13,17 @@ import { registerCompute } from './compute-registry.js';
|
|
|
13
13
|
* compute in an app runs the same backend and agrees on the resource-name
|
|
14
14
|
* namespace.
|
|
15
15
|
*
|
|
16
|
+
* **Observability is compute-owned at deploy time.** Logging is always on (a
|
|
17
|
+
* compute owns its log group and captures stdout; retention is a compute-level
|
|
18
|
+
* setting), so there is no "enable logging" — logs always exist. Tracing, by
|
|
19
|
+
* contrast, provisions real infrastructure (X-Ray) and has cost, so it is
|
|
20
|
+
* enabled explicitly via {@link enableTracing} — which the framework calls on
|
|
21
|
+
* every compute when the app contains a `Tracer` (presence-gated).
|
|
22
|
+
*
|
|
16
23
|
* The abstract base lives in core (a framework primitive); concrete computes
|
|
17
24
|
* live in their own packages (e.g. `LambdaCompute` in `@aws-blocks/bb-lambda-compute`).
|
|
18
25
|
*
|
|
19
|
-
* @internal Not exported from the package's public entry points.
|
|
20
|
-
* cannot instantiate a compute until the customer-facing surface exists.
|
|
26
|
+
* @internal Not exported from the package's public entry points.
|
|
21
27
|
*/
|
|
22
28
|
export class Compute extends Scope {
|
|
23
29
|
/**
|
|
@@ -26,11 +32,59 @@ export class Compute extends Scope {
|
|
|
26
32
|
* unpopulated (no compute assignment surface yet).
|
|
27
33
|
*/
|
|
28
34
|
namespaces = [];
|
|
35
|
+
/**
|
|
36
|
+
* Whether tracing has been enabled on this compute — flipped by
|
|
37
|
+
* {@link enableTracing}. Private so it can't be set independently of the
|
|
38
|
+
* infra; read internally by {@link dashboardSection} to decide whether to
|
|
39
|
+
* render the traces section.
|
|
40
|
+
*/
|
|
41
|
+
tracerEnabled = false;
|
|
42
|
+
/** Read-only view of {@link tracerEnabled} for subclasses (e.g. to guard
|
|
43
|
+
* their `tracingWidgets` builder). Subclasses can read but not set it. */
|
|
44
|
+
get isTracerEnabled() {
|
|
45
|
+
return this.tracerEnabled;
|
|
46
|
+
}
|
|
29
47
|
constructor(id, options) {
|
|
30
48
|
super(id, options);
|
|
31
|
-
// Self-register on the owning stack so finalize steps (
|
|
49
|
+
// Self-register on the owning stack so finalize steps (tracing, routing,
|
|
32
50
|
// dashboards) can enumerate every compute without a separate discovery
|
|
33
51
|
// pass. Scoped per stack, so a multi-stack synth keeps lists isolated.
|
|
34
52
|
registerCompute(this);
|
|
35
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Enable distributed tracing on this compute: mark it traced (so the Dashboard
|
|
56
|
+
* renders its traces section) and turn on the compute's active tracing via
|
|
57
|
+
* {@link applyTracing}. Idempotent — the framework calls this on **every**
|
|
58
|
+
* compute when the app contains a `Tracer` (tracing is presence-gated, not
|
|
59
|
+
* per-compute), so calling it more than once is a no-op.
|
|
60
|
+
*/
|
|
61
|
+
enableTracing() {
|
|
62
|
+
if (this.tracerEnabled)
|
|
63
|
+
return;
|
|
64
|
+
this.tracerEnabled = true;
|
|
65
|
+
this.applyTracing();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build this compute's CloudWatch Dashboard section: health widgets and log
|
|
69
|
+
* widgets **always** (logs are always captured for a compute), plus trace
|
|
70
|
+
* widgets **only when** tracing is enabled on this compute (via
|
|
71
|
+
* {@link enableTracing}).
|
|
72
|
+
*
|
|
73
|
+
* This is the single public entry the Dashboard Building Block uses; the
|
|
74
|
+
* per-kind builders below are `protected`. Whether the logs / traces sections
|
|
75
|
+
* are actually shown is a display choice the Dashboard makes on top (its
|
|
76
|
+
* `logs` / `traces` options) — this returns what the compute *has*.
|
|
77
|
+
*
|
|
78
|
+
* @param region - AWS region the widgets query metrics in.
|
|
79
|
+
*/
|
|
80
|
+
dashboardSection(region) {
|
|
81
|
+
return {
|
|
82
|
+
// The scope id (e.g. 'DefaultCompute', 'api') — short and readable for a
|
|
83
|
+
// section header, and distinct per compute within a stack.
|
|
84
|
+
label: this.id,
|
|
85
|
+
health: this.healthWidgets(region),
|
|
86
|
+
logging: this.loggingWidgets(region),
|
|
87
|
+
tracing: this.tracerEnabled ? this.tracingWidgets(region) : undefined,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
36
90
|
}
|
|
@@ -37,6 +37,18 @@ class TestCompute extends Compute {
|
|
|
37
37
|
setEnv(key, value) {
|
|
38
38
|
this.fn.addEnvironment(key, value);
|
|
39
39
|
}
|
|
40
|
+
// Observability hooks are irrelevant to config-registry tests — stub them so
|
|
41
|
+
// this test double satisfies Compute's abstract contract.
|
|
42
|
+
applyTracing() { }
|
|
43
|
+
healthWidgets() {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
loggingWidgets() {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
tracingWidgets() {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
40
52
|
}
|
|
41
53
|
function stackWithCompute(id) {
|
|
42
54
|
const app = new cdk.App();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Construct } from 'constructs';
|
|
2
|
+
/** A Dashboard's deferred widget-body build, run once after the app is constructed. */
|
|
3
|
+
type DashboardFinalizer = () => void;
|
|
4
|
+
/**
|
|
5
|
+
* Register a Dashboard's deferred body-build, to run after every Building Block
|
|
6
|
+
* in the app has been constructed (the end of `BlocksStack`/`BlocksBackend`
|
|
7
|
+
* `create()`, once the backend module has fully imported).
|
|
8
|
+
*
|
|
9
|
+
* The Dashboard builds its widget body here rather than in its constructor
|
|
10
|
+
* because the body depends on which computes are traced, and a `Tracer` may be
|
|
11
|
+
* constructed *after* the Dashboard. Deferring makes the Dashboard observe the
|
|
12
|
+
* complete app, so it never depends on construction order. (The Dashboard's
|
|
13
|
+
* CloudWatch resource itself is created eagerly in the constructor, so its URL /
|
|
14
|
+
* redirect route never dangle — only the body is deferred.)
|
|
15
|
+
*
|
|
16
|
+
* This is intentionally scoped to the Dashboard (the only deferred-build case
|
|
17
|
+
* today) rather than a generic finalizer mechanism; generalize it only if a
|
|
18
|
+
* second use case appears.
|
|
19
|
+
*
|
|
20
|
+
* @param scope - Any construct in the stack (used to locate the stack).
|
|
21
|
+
* @param finalize - The deferred body-build; run once (in registration order)
|
|
22
|
+
* by {@link finalizeDashboards}.
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerDashboardFinalizer(scope: Construct, finalize: DashboardFinalizer): void;
|
|
25
|
+
/**
|
|
26
|
+
* Run — and clear — every registered Dashboard finalizer on `scope`'s stack, in
|
|
27
|
+
* registration order. Called once from `create()` after the backend module has
|
|
28
|
+
* imported. Clearing the list makes a repeated call a no-op, so a dashboard's
|
|
29
|
+
* body is never built twice.
|
|
30
|
+
*
|
|
31
|
+
* A Dashboard constructed outside `create()` (e.g. directly in a unit test) must
|
|
32
|
+
* call this explicitly before synth — the same way `config-registry.test.ts`
|
|
33
|
+
* drives `finalizeConfigRegistry`. (A Dashboard's CloudWatch resource is created
|
|
34
|
+
* eagerly in its constructor, so even if its finalizer never runs its URL /
|
|
35
|
+
* redirect never dangle — only the widget body is missing.)
|
|
36
|
+
*
|
|
37
|
+
* @param scope - Any construct in the stack (used to locate the stack).
|
|
38
|
+
*/
|
|
39
|
+
export declare function finalizeDashboards(scope: Construct): void;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=dashboard-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-registry.d.ts","sourceRoot":"","sources":["../../src/cdk/dashboard-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,uFAAuF;AACvF,KAAK,kBAAkB,GAAG,MAAM,IAAI,CAAC;AAgBrC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAE/F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAKzD"}
|