Skip to content

Commit 71ecf77

Browse files
committed
feat(toolkit-lib): generic messages do not have a code
1 parent 0145866 commit 71ecf77

File tree

18 files changed

+161
-82
lines changed

18 files changed

+161
-82
lines changed

packages/@aws-cdk/toolkit-lib/docs/message-registry.md

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,66 @@
11
---
2-
title: IoMessages Registry
2+
title: Messages and payloads
33
group: Documents
44
---
5-
# IoMessages Registry
5+
# Messages and payloads
66

7-
| Code | Description | Level | Data Interface |
8-
|------|-------------|-------|----------------|
7+
The CDK Toolkit emits *messages* and *requests* to structure interactions.
8+
A *request* is a special *message* that allows the receiver to respond, if no response is returned the toolkit will continue with a default.
9+
Messages are unidirectional and always send from the CDK Toolkit to your integration.
10+
11+
All messages include text that is suitable for display to an end-user or logging.
12+
Some messages also include a unique `code` and a additional payload `data`, providing you with structured information for your integration.
13+
*Requests* always have a `code` and can be addressed explicitly.
14+
See {@link IoMessage} and {@link IoRequest} for a complete list of available fields.
15+
16+
## Levels
17+
18+
Messages have a `level` assigned to them.
19+
Levels are ordered by their importance, with `error` being the most and `trace` being the least important.
20+
21+
| Level | Description |
22+
| ---------- | ---------------------------------------------- |
23+
| `error` | Error messages that may affect operation. |
24+
| `result` | Primary message of an operation. |
25+
| `warn` | Warning messages that don't prevent operation. |
26+
| `info` | General informational messages. |
27+
| `debug` | Detailed messages for troubleshooting. |
28+
| `trace` | Very detailed execution flow information. |
29+
30+
Attached levels are an informal recommendation of what *we* believe is the relevance of a specific message.
31+
Your integration will always receive all messages of all levels.
32+
It is up to you to filter out irrelevant messages.
33+
For standard operations, we recommend to display all messages with level `info` or above.
34+
35+
## Backwards compatibility
36+
37+
Messages and requests are an essential part of the CDK Toolkit's public contract.
38+
We recognize integrators will build critical workflows depending on these structured interactions.
39+
To help integrators build with confidence, we provide clear expectations with regards to backwards compatibility of messages.
40+
41+
**Depend only on messages and requests with a `code`. Treat all other messages as informational only.**
42+
If a message does not have a code, it can change or disappear at any time without notice.
43+
44+
**Only the `code` and `data` properties of a message are in scope for backwards compatibility.**
45+
Payload data can change, but we will only make type-compatible, additive changes.
46+
For example we may add new data, but will not remove information.
47+
48+
For the avoidance of doubt, the following changes are explicitly not considered breaking:
49+
50+
- a change to the message text or level,
51+
- a change to the default response of a request,
52+
- a change to the order messages and requests are emitted in,
53+
- the addition of new messages and requests, and
54+
- the removal of messages without a code
55+
56+
## Registry
57+
58+
This is the complete list of all currently available messages with codes and their respective payload interface.
59+
We are welcoming requests for additional coded messages and data.
60+
Please let us know by [opening an issue](https://github.com/aws/aws-cdk-cli/issues/new/choose).
61+
62+
| Code | Description | Level | Payload data interface |
63+
|------|-------------|-------|------------------------|
964
| `CDK_TOOLKIT_W0100` | Credential plugin warnings | `warn` | n/a |
1065
| `CDK_TOOLKIT_I1000` | Provides synthesis times. | `info` | {@link Duration} |
1166
| `CDK_TOOLKIT_I1001` | Cloud Assembly synthesis is starting | `trace` | {@link StackSelectionDetails} |

packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/awscli-compatible.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export class AwsCliCompatible {
156156

157157
if (!region) {
158158
const usedProfile = !profile ? '' : ` (profile: "${profile}")`;
159-
await this.ioHelper.sdkDefaults.debug(
159+
await this.ioHelper.defaults.debug(
160160
`Unable to determine AWS region from environment or AWS configuration${usedProfile}, defaulting to '${defaultRegion}'`,
161161
);
162162
return defaultRegion;
@@ -173,7 +173,7 @@ export class AwsCliCompatible {
173173
* @returns The region for the instance identity
174174
*/
175175
private async regionFromMetadataService() {
176-
await this.ioHelper.sdkDefaults.debug('Looking up AWS region in the EC2 Instance Metadata Service (IMDS).');
176+
await this.ioHelper.defaults.debug('Looking up AWS region in the EC2 Instance Metadata Service (IMDS).');
177177
try {
178178
const metadataService = new MetadataService({
179179
httpOptions: {
@@ -185,7 +185,7 @@ export class AwsCliCompatible {
185185
const document = await metadataService.request('/latest/dynamic/instance-identity/document', {});
186186
return JSON.parse(document).region;
187187
} catch (e) {
188-
await this.ioHelper.sdkDefaults.debug(`Unable to retrieve AWS region from IMDS: ${e}`);
188+
await this.ioHelper.defaults.debug(`Unable to retrieve AWS region from IMDS: ${e}`);
189189
}
190190
}
191191

@@ -223,7 +223,7 @@ export class AwsCliCompatible {
223223
* Result is send to callback function for SDK to authorize the request
224224
*/
225225
private async tokenCodeFn(deviceArn: string): Promise<string> {
226-
const debugFn = (msg: string, ...args: any[]) => this.ioHelper.sdkDefaults.debug(format(msg, ...args));
226+
const debugFn = (msg: string, ...args: any[]) => this.ioHelper.defaults.debug(format(msg, ...args));
227227
await debugFn('Require MFA token from MFA device with ARN', deviceArn);
228228
try {
229229
const token: string = await this.ioHelper.requestResponse(IO.CDK_SDK_I1100.req(`MFA token for ${deviceArn}`, {

packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/proxy-agent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ export class ProxyAgentProvider {
2626
private async tryGetCACert(bundlePath?: string) {
2727
const path = bundlePath || this.caBundlePathFromEnvironment();
2828
if (path) {
29-
await this.ioHelper.sdkDefaults.debug(`Using CA bundle path: ${path}`);
29+
await this.ioHelper.defaults.debug(`Using CA bundle path: ${path}`);
3030
try {
3131
if (!fs.pathExistsSync(path)) {
3232
return undefined;
3333
}
3434
return fs.readFileSync(path, { encoding: 'utf-8' });
3535
} catch (e: any) {
36-
await this.ioHelper.sdkDefaults.debug(String(e));
36+
await this.ioHelper.defaults.debug(String(e));
3737
return undefined;
3838
}
3939
}

packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/sdk-provider.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ export class SdkProvider {
173173
// feed the CLI credentials which are sufficient by themselves. Prefer to assume the correct role if we can,
174174
// but if we can't then let's just try with available credentials anyway.
175175
if (baseCreds.source === 'correctDefault' || baseCreds.source === 'plugin') {
176-
await this.ioHelper.sdkDefaults.debug(err.message);
176+
await this.ioHelper.defaults.debug(err.message);
177177

178178
const level = quiet ? 'debug' : 'warn';
179-
await this.ioHelper.sdkDefaults[level](
179+
await this.ioHelper.defaults[level](
180180
`${fmtObtainedCredentials(baseCreds)} could not be used to assume '${options.assumeRoleArn}', but are for the right account. Proceeding anyway.`,
181181
);
182182
return {
@@ -252,13 +252,13 @@ export class SdkProvider {
252252
// they are complaining about if we fail 'cdk synth' on them. We loudly complain in order to show that
253253
// the current situation is probably undesirable, but we don't fail.
254254
if (e.name === 'ExpiredToken') {
255-
await this.ioHelper.sdkDefaults.warn(
255+
await this.ioHelper.defaults.warn(
256256
'There are expired AWS credentials in your environment. The CDK app will synth without current account information.',
257257
);
258258
return undefined;
259259
}
260260

261-
await this.ioHelper.sdkDefaults.debug(`Unable to determine the default AWS account (${e.name}): ${formatErrorMessage(e)}`);
261+
await this.ioHelper.defaults.debug(`Unable to determine the default AWS account (${e.name}): ${formatErrorMessage(e)}`);
262262
return undefined;
263263
}
264264
});
@@ -320,7 +320,7 @@ export class SdkProvider {
320320
additionalOptions?: AssumeRoleAdditionalOptions,
321321
region?: string,
322322
): Promise<SDK> {
323-
await this.ioHelper.sdkDefaults.debug(`Assuming role '${roleArn}'.`);
323+
await this.ioHelper.defaults.debug(`Assuming role '${roleArn}'.`);
324324

325325
region = region ?? this.defaultRegion;
326326

@@ -354,7 +354,7 @@ export class SdkProvider {
354354
throw err;
355355
}
356356

357-
await this.ioHelper.sdkDefaults.debug(`Assuming role failed: ${err.message}`);
357+
await this.ioHelper.defaults.debug(`Assuming role failed: ${err.message}`);
358358
throw new AuthenticationError(
359359
[
360360
'Could not assume role in target account',

packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/sdk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ export class SDK {
593593
ioHelper: IoHelper,
594594
logger?: ISdkLogger,
595595
) {
596-
const debugFn = async (msg: string) => ioHelper.sdkDefaults.debug(msg);
596+
const debugFn = async (msg: string) => ioHelper.defaults.debug(msg);
597597
this.accountCache = new AccountAccessKeyCache(AccountAccessKeyCache.DEFAULT_PATH, debugFn);
598598
this.debug = debugFn;
599599
this.config = {

packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/prepare-source.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class ExecutionEnvironment implements AsyncDisposable {
6060
) {
6161
this.ioHelper = services.ioHelper;
6262
this.sdkProvider = services.sdkProvider;
63-
this.debugFn = (msg: string) => this.ioHelper.assemblyDefaults.debug(msg);
63+
this.debugFn = (msg: string) => this.ioHelper.defaults.debug(msg);
6464
this.lock = lock;
6565
this.shouldClean = outDirIsTemporary;
6666
}
@@ -228,7 +228,7 @@ export class ExecutionEnvironment implements AsyncDisposable {
228228
* @param assembly the assembly to check
229229
*/
230230
async function checkContextOverflowSupport(assembly: cxapi.CloudAssembly, ioHelper: IoHelper): Promise<void> {
231-
const traceFn = (msg: string) => ioHelper.assemblyDefaults.trace(msg);
231+
const traceFn = (msg: string) => ioHelper.defaults.trace(msg);
232232
const tree = await loadTree(assembly, traceFn);
233233
const frameworkDoesNotSupportContextOverflow = some(tree, node => {
234234
const fqn = node.constructInfo?.fqn;

packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/stack-assembly.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ async function includeDownstreamStacks(
148148
} while (madeProgress);
149149

150150
if (added.length > 0) {
151-
await ioHelper.assemblyDefaults.info(`Including depending stacks: ${chalk.bold(added.join(', '))}`);
151+
await ioHelper.defaults.info(`Including depending stacks: ${chalk.bold(added.join(', '))}`);
152152
}
153153
}
154154

@@ -180,6 +180,6 @@ async function includeUpstreamStacks(
180180
}
181181

182182
if (added.length > 0) {
183-
await ioHelper.assemblyDefaults.info(`Including dependency stacks: ${chalk.bold(added.join(', '))}`);
183+
await ioHelper.defaults.info(`Including dependency stacks: ${chalk.bold(added.join(', '))}`);
184184
}
185185
}

packages/@aws-cdk/toolkit-lib/lib/api/io/io-message.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,21 @@ export interface IoMessage<T> {
3636
/**
3737
* A short message code uniquely identifying a message type using the format CDK_[CATEGORY]_[E/W/I][0000-9999].
3838
*
39+
* Every code releates to a message with a specific payload.
40+
* Messages without code are considered generic and do not have a payload.
41+
*
3942
* The level indicator follows these rules:
4043
* - 'E' for error level messages
4144
* - 'W' for warning level messages
4245
* - 'I' for info/debug/trace level messages
4346
*
44-
* Codes ending in 000 0 are generic messages, while codes ending in 0001-9999 are specific to a particular message.
45-
* The following are examples of valid and invalid message codes:
46-
* ```ts
47-
* 'CDK_ASSETS_I0000' // valid: generic assets info message
48-
* 'CDK_TOOLKIT_E0002' // valid: specific toolkit error message
49-
* 'CDK_SDK_W0023' // valid: specific sdk warning message
50-
* ```
51-
*
52-
* @see https://github.com/aws/aws-cdk-cli/blob/main/packages/%40aws-cdk/toolkit-lib/CODE_REGISTRY.md
47+
* @see https://docs.aws.amazon.com/cdk/api/toolkit-lib/message-registry/
5348
*/
54-
readonly code: IoMessageCode;
49+
readonly code?: IoMessageCode;
5550

5651
/**
5752
* The message text.
53+
*
5854
* This is safe to print to an end-user.
5955
*/
6056
readonly message: string;
@@ -83,4 +79,6 @@ export interface IoRequest<T, U> extends IoMessage<T> {
8379
* The default response that will be used if no data is returned.
8480
*/
8581
readonly defaultResponse: U;
82+
83+
readonly code: IoMessageCode;
8684
}

packages/@aws-cdk/toolkit-lib/lib/api/io/private/io-default-messages.ts

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as util from 'util';
2-
import type { ActionLessMessage, ActionLessRequest, IoHelper } from './io-helper';
3-
import type { IoMessageCode, IoMessageLevel } from '../io-message';
2+
import type { ActionLessMessage, IoHelper } from './io-helper';
3+
import type { IoMessageLevel } from '../io-message';
44

55
/**
66
* Helper class to emit standard log messages to an IoHost
@@ -10,24 +10,18 @@ import type { IoMessageCode, IoMessageLevel } from '../io-message';
1010
*/
1111
export class IoDefaultMessages {
1212
private readonly ioHelper: IoHelper;
13-
private readonly category: 'TOOLKIT' | 'ASSEMBLY' | 'SDK';
1413

15-
constructor(ioHelper: IoHelper, category: 'TOOLKIT' | 'ASSEMBLY' | 'SDK') {
14+
constructor(ioHelper: IoHelper) {
1615
this.ioHelper = ioHelper;
17-
this.category = category;
1816
}
1917

2018
public async notify(msg: Omit<ActionLessMessage<unknown>, 'code'>): Promise<void> {
2119
return this.ioHelper.notify({
2220
...msg,
23-
code: levelToCode(this.category, msg.level),
21+
code: undefined,
2422
});
2523
}
2624

27-
public async requestResponse<T, U>(msg: ActionLessRequest<T, U>): Promise<U> {
28-
return this.ioHelper.requestResponse(msg);
29-
}
30-
3125
public async error(input: string, ...args: unknown[]): Promise<void> {
3226
return this.emitMessage('error', input, ...args);
3327
}
@@ -65,7 +59,6 @@ export class IoDefaultMessages {
6559

6660
return {
6761
time: new Date(),
68-
code: levelToCode(this.category, level),
6962
level,
7063
message,
7164
data: undefined,
@@ -76,14 +69,3 @@ export class IoDefaultMessages {
7669
return this.ioHelper.notify(this.msg(level, input, ...args));
7770
}
7871
}
79-
80-
function levelToCode(category: string, level: IoMessageLevel): IoMessageCode {
81-
switch (level) {
82-
case 'error':
83-
return `CDK_${category}_E0000`;
84-
case 'warn':
85-
return `CDK_${category}_W0000`;
86-
default:
87-
return `CDK_${category}_I0000`;
88-
}
89-
}

packages/@aws-cdk/toolkit-lib/lib/api/io/private/io-helper.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,14 @@ export class IoHelper implements IIoHost {
2020
* Simplified access to emit default messages.
2121
*/
2222
public readonly defaults: IoDefaultMessages;
23-
public readonly assemblyDefaults: IoDefaultMessages;
24-
public readonly sdkDefaults: IoDefaultMessages;
2523

2624
private readonly ioHost: IIoHost;
2725
private readonly action: ToolkitAction;
2826

2927
private constructor(ioHost: IIoHost, action: ToolkitAction) {
3028
this.ioHost = ioHost;
3129
this.action = action;
32-
this.defaults = new IoDefaultMessages(this, 'TOOLKIT');
33-
this.assemblyDefaults = new IoDefaultMessages(this, 'ASSEMBLY');
34-
this.sdkDefaults = new IoDefaultMessages(this, 'SDK');
30+
this.defaults = new IoDefaultMessages(this);
3531
}
3632

3733
/**

0 commit comments

Comments
 (0)