Skip to content

Commit 84d0b54

Browse files
authored
feat(toolkit-lib): emit marker messages during actions (#219)
Introduces the new concept of "Message Spans" to connect multiple messages for toolkit sub-tasks like "synth" or "build-assets" together. A span consists of a number of messages that have the same `span` value. At a minimum, a span is a pair of a `start` and an `end` message. Every end message also reports the elapsed time since the start of the span. Message spans will allow integrators to more easily track the flow of "tasks" completed in the Toolkit. ## Changes - Refactors the previous `Timer` class to "Message Spans" - Introduces a new registry for spans at the bottom of the existing messages registry. A span consists of a start and an end message maker. - This PR makes use of the new `IoHelper` to provide a nicer API - There is some more of type magic to ensure correct payloads, I don't think this can be tested further - I also changed the message registry to be private again, we are not ready to have it as part of the public interface (this doesn't affect our packages) - Made some improvements to the bundling of declaration files. It's currently really slow because it ends up scanning through A LOT of SDK types. Will look into this more in a follow-up PR. Fixes aws/aws-cdk#33286 --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license
1 parent 1b22ebc commit 84d0b54

File tree

31 files changed

+583
-252
lines changed

31 files changed

+583
-252
lines changed

.projenrc.ts

+1
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ const tmpToolkitHelpers = configureProject(
705705
'archiver',
706706
'glob',
707707
'semver',
708+
'uuid',
708709
'yaml@^1',
709710
],
710711
tsconfig: {

packages/@aws-cdk/tmp-toolkit-helpers/.projen/deps.json

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/tmp-toolkit-helpers/.projen/tasks.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/tmp-toolkit-helpers/package.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './stack-selector';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Which stacks should be selected from a cloud assembly
3+
*/
4+
export enum StackSelectionStrategy {
5+
/**
6+
* Returns all stacks in the app regardless of patterns,
7+
* including stacks inside nested assemblies.
8+
*/
9+
ALL_STACKS = 'all-stacks',
10+
11+
/**
12+
* Returns all stacks in the main (top level) assembly only.
13+
*/
14+
MAIN_ASSEMBLY = 'main-assembly',
15+
16+
/**
17+
* If the assembly includes a single stack, returns it.
18+
* Otherwise throws an exception.
19+
*/
20+
ONLY_SINGLE = 'only-single',
21+
22+
/**
23+
* Return stacks matched by patterns.
24+
* If no stacks are found, execution is halted successfully.
25+
* Most likely you don't want to use this but `StackSelectionStrategy.MUST_MATCH_PATTERN`
26+
*/
27+
PATTERN_MATCH = 'pattern-match',
28+
29+
/**
30+
* Return stacks matched by patterns.
31+
* Throws an exception if the patterns don't match at least one stack in the assembly.
32+
*/
33+
PATTERN_MUST_MATCH = 'pattern-must-match',
34+
35+
/**
36+
* Returns if exactly one stack is matched by the pattern(s).
37+
* Throws an exception if no stack, or more than exactly one stack are matched.
38+
*/
39+
PATTERN_MUST_MATCH_SINGLE = 'pattern-must-match-single',
40+
}
41+
42+
/**
43+
* When selecting stacks, what other stacks to include because of dependencies
44+
*/
45+
export enum ExpandStackSelection {
46+
/**
47+
* Don't select any extra stacks
48+
*/
49+
NONE = 'none',
50+
51+
/**
52+
* Include stacks that this stack depends on
53+
*/
54+
UPSTREAM = 'upstream',
55+
56+
/**
57+
* Include stacks that depend on this stack
58+
*/
59+
DOWNSTREAM = 'downstream',
60+
61+
/**
62+
* @TODO
63+
* Include both directions.
64+
* I.e. stacks that this stack depends on, and stacks that depend on this stack.
65+
*/
66+
// FULL = 'full',
67+
}
68+
69+
/**
70+
* A specification of which stacks should be selected
71+
*/
72+
export interface StackSelector {
73+
/**
74+
* The behavior if if no selectors are provided.
75+
*/
76+
strategy: StackSelectionStrategy;
77+
78+
/**
79+
* A list of patterns to match the stack hierarchical ids
80+
* Only used with `PATTERN_*` selection strategies.
81+
*/
82+
patterns?: string[];
83+
84+
/**
85+
* Expand the selection to upstream/downstream stacks.
86+
* @default ExpandStackSelection.None only select the specified/matched stacks
87+
*/
88+
expand?: ExpandStackSelection;
89+
90+
/**
91+
* By default, we throw an exception if the assembly contains no stacks.
92+
* Set to `false`, to halt execution for empty assemblies without error.
93+
*
94+
* Note that actions can still throw if a stack selection result is empty,
95+
* but the assembly contains stacks in principle.
96+
*
97+
* @default true
98+
*/
99+
failOnEmpty?: boolean;
100+
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './cloud-assembly';
12
export * from './io';
23
export * from './toolkit-error';

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ export * from './io-host';
22
export * from './io-message';
33
export * from './toolkit-action';
44
export * from './payloads';
5-
export * from './messages';

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/io-message.ts

+10
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ export interface IoMessage<T> {
5959
*/
6060
readonly message: string;
6161

62+
/**
63+
* Identifies the message span, this message belongs to.
64+
*
65+
* A message span, groups multiple messages together that semantically related to the same operation.
66+
* This is an otherwise meaningless identifier.
67+
*
68+
* A message without a `spanId`, does not belong to a span.
69+
*/
70+
readonly span?: string;
71+
6272
/**
6373
* The data attached to the message.
6474
*/

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/deploy.ts

+44
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
2+
import type { IManifestEntry } from 'cdk-assets';
23
import type { PermissionChangeType } from './diff';
34
import type { ConfirmationRequest } from './types';
45

@@ -55,3 +56,46 @@ export interface NeedRollbackFirstDeployStackResult {
5556
export interface ReplacementRequiresRollbackStackResult {
5657
readonly type: 'replacement-requires-rollback';
5758
}
59+
60+
export interface StackDeployProgress {
61+
/**
62+
* The total number of stacks being deployed
63+
*/
64+
readonly total: number;
65+
/**
66+
* The count of the stack currently attempted to be deployed
67+
*
68+
* This is counting value, not an identifier.
69+
*/
70+
readonly current: number;
71+
/**
72+
* The stack that's currently being deployed
73+
*/
74+
readonly stack: CloudFormationStackArtifact;
75+
}
76+
77+
/**
78+
* Payload for a yes/no confirmation in deploy. Includes information on
79+
* what kind of change is being made.
80+
*/
81+
export interface DeployConfirmationRequest extends ConfirmationRequest {
82+
/**
83+
* The type of change being made to the IAM permissions.
84+
*/
85+
readonly permissionChangeType: PermissionChangeType;
86+
}
87+
88+
export interface BuildAsset {
89+
/**
90+
* The asset that is build
91+
*/
92+
readonly asset: IManifestEntry;
93+
}
94+
95+
export interface PublishAsset {
96+
97+
/**
98+
* The asset that is published
99+
*/
100+
readonly asset: IManifestEntry;
101+
}

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/destroy.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import type { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
22

3+
export interface StackDestroy {
4+
/**
5+
* The stacks that will be destroyed
6+
*/
7+
readonly stacks: CloudFormationStackArtifact[];
8+
}
9+
310
export interface StackDestroyProgress {
411
/**
512
* The total number of stacks being destroyed

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './sdk-trace';
66
export * from './context';
77
export * from './rollback';
88
export * from './stack-activity';
9+
export * from './synth';
910
export * from './types';
1011
export * from './progress';
1112
export * from './watch';

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/stack-activity.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { type MetadataEntry } from '@aws-cdk/cloud-assembly-schema';
2-
import { type CloudFormationStackArtifact } from '@aws-cdk/cx-api';
1+
import type { MetadataEntry } from '@aws-cdk/cloud-assembly-schema';
2+
import type { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
33
import type { StackEvent } from '@aws-sdk/client-cloudformation';
44
import type { StackProgress } from './progress';
55

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { StackSelector } from '../../cloud-assembly/stack-selector';
2+
3+
export interface StackSelectionDetails {
4+
/**
5+
* The selected stacks, if any
6+
*/
7+
readonly stacks: StackSelector;
8+
}

packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/watch.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/**
32
* The computed file watch settings
43
*/
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from './io-helper';
22
export * from './level-priority';
3+
export * from './span';
34
export * from './message-maker';
5+
export * from './messages';
46
export * from './types';
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,59 @@
11
import type { IIoHost } from '../io-host';
22
import type { IoMessage, IoRequest } from '../io-message';
33
import type { ToolkitAction } from '../toolkit-action';
4+
import type { SpanEnd, SpanDefinition } from './span';
5+
import { SpanMaker } from './span';
46

5-
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
6-
export type SimplifiedMessage<T> = Pick<IoMessage<T>, 'level' | 'code' | 'message' | 'data'>;
77
export type ActionLessMessage<T> = Omit<IoMessage<T>, 'action'>;
88
export type ActionLessRequest<T, U> = Omit<IoRequest<T, U>, 'action'>;
99

1010
/**
11-
* Helper for IO messaging.
12-
*
13-
* Wraps a client provided IoHost and provides additional features & services to toolkit internal classes.
11+
* A class containing helper tools to interact with IoHost
1412
*/
15-
export interface IoHelper extends IIoHost {
16-
notify(msg: ActionLessMessage<unknown>): Promise<void>;
17-
notify<T>(msg: ActionLessMessage<T>): Promise<void>;
18-
requestResponse<T, U>(msg: ActionLessRequest<T, U>): Promise<U>;
13+
export class IoHelper implements IIoHost {
14+
public static fromIoHost(ioHost: IIoHost, action: ToolkitAction) {
15+
return new IoHelper(ioHost, action);
16+
}
17+
18+
private readonly ioHost: IIoHost;
19+
private readonly action: ToolkitAction;
20+
21+
private constructor(ioHost: IIoHost, action: ToolkitAction) {
22+
this.ioHost = ioHost;
23+
this.action = action;
24+
}
25+
26+
/**
27+
* Forward a message to the IoHost, while injection the current action
28+
*/
29+
public notify(msg: ActionLessMessage<unknown>): Promise<void> {
30+
return this.ioHost.notify({
31+
...msg,
32+
action: this.action,
33+
});
34+
}
35+
36+
/**
37+
* Forward a request to the IoHost, while injection the current action
38+
*/
39+
public requestResponse<T, U>(msg: ActionLessRequest<T, U>): Promise<U> {
40+
return this.ioHost.requestResponse({
41+
...msg,
42+
action: this.action,
43+
});
44+
}
45+
46+
/**
47+
* Create a new marker from a given registry entry
48+
*/
49+
public span<S extends object, E extends SpanEnd>(definition: SpanDefinition<S, E>) {
50+
return new SpanMaker(this, definition);
51+
}
1952
}
2053

2154
/**
2255
* Wraps an IoHost and creates an IoHelper from it
2356
*/
2457
export function asIoHelper(ioHost: IIoHost, action: ToolkitAction): IoHelper {
25-
return {
26-
notify: async <T>(msg: Omit<IoMessage<T>, 'action'>) => {
27-
await ioHost.notify({
28-
...msg,
29-
action,
30-
});
31-
},
32-
requestResponse: async <T, U>(msg: Omit<IoRequest<T, U>, 'action'>) => {
33-
return ioHost.requestResponse({
34-
...msg,
35-
action,
36-
});
37-
},
38-
};
58+
return IoHelper.fromIoHost(ioHost, action);
3959
}

0 commit comments

Comments
 (0)