Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): acknowledge warnings #26144

Merged
merged 35 commits into from
Aug 23, 2023
Merged
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5ce3b70
feat(core): acknowledge annotation warning messages
corymhall Jun 27, 2023
fca3a1a
adding deprecation
corymhall Jun 28, 2023
707be7f
updating all references from `addWarning` to `addWarningV2`
corymhall Jun 29, 2023
5229dc9
updated readme
corymhall Jun 29, 2023
bb217e3
fixing lint errors
corymhall Jun 29, 2023
9e19207
fixing tests
corymhall Jun 29, 2023
a9f506e
Merge branch 'main' into corymhall/core/ack-warnings
corymhall Jun 29, 2023
4b8ad28
adding to breaking change
corymhall Jun 29, 2023
eb244d8
couple more
corymhall Jun 29, 2023
54a2910
adding examples
corymhall Jun 30, 2023
06f8559
adding adr
corymhall Jul 5, 2023
4af1135
formatting the messages
corymhall Jul 5, 2023
9198add
Merge remote-tracking branch 'origin/main' into pr/corymhall/26144
rix0rrr Aug 18, 2023
957c238
Keep warning suppression synth-side by means of an App global
rix0rrr Aug 18, 2023
f0ce23b
Don't need these breaking changes anymore
rix0rrr Aug 18, 2023
6412029
Comment
rix0rrr Aug 18, 2023
df8a853
Update adr
rix0rrr Aug 18, 2023
5aba9ac
Undo test change
rix0rrr Aug 18, 2023
ee9fc76
addWarning -> addWarningV2
rix0rrr Aug 18, 2023
56625ac
Linterrrr
rix0rrr Aug 18, 2023
cd72788
Update tests
rix0rrr Aug 21, 2023
87b3b12
More tests
rix0rrr Aug 21, 2023
c0b5319
Oops
rix0rrr Aug 21, 2023
a95dde9
Test again
rix0rrr Aug 21, 2023
acca007
Merge branch 'main' into corymhall/core/ack-warnings
rix0rrr Aug 21, 2023
da5211d
Foutje
rix0rrr Aug 22, 2023
9e9a6fd
Merge branch 'corymhall/core/ack-warnings' of github.com:corymhall/aw…
rix0rrr Aug 22, 2023
52ecb54
Merge branch 'main' into corymhall/core/ack-warnings
rix0rrr Aug 22, 2023
7c5b1c7
Merge branch 'corymhall/core/ack-warnings' of github.com:corymhall/aw…
rix0rrr Aug 22, 2023
4fb2770
This is not a warning
rix0rrr Aug 22, 2023
af14a3f
Merge branch 'main' into corymhall/core/ack-warnings
rix0rrr Aug 23, 2023
696945d
Exclude "__proto__" from fast-check
rix0rrr Aug 23, 2023
6361023
Fix examples
rix0rrr Aug 23, 2023
52eab30
Merge branch 'main' into corymhall/core/ack-warnings
rix0rrr Aug 23, 2023
e8d78d6
Merge branch 'main' into corymhall/core/ack-warnings
rix0rrr Aug 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions packages/aws-cdk-lib/core/lib/adr/acknowledge-warnings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Acknowledge Warnings

## Status

accepted

## Context

Construct authors can add messages that will be printed at synthesis. It
possible to add three types of warnings:

- `INFO`: Prints the info on synthesis
- `WARN`: Prints the warning on synthesis. Can also throw an error if the `--strict`
argument is used
- `ERROR`: Throws an error with the message

Warning messages are typically used for two types of messages:

1. Things that are errors (and will error on deployment) in some cases, but not all. For example, a warning is
used when > 10 managed policies are added to an IAM Role because that is the
initial limit for AWS accounts. An error is not used because it is possible
to increase the limit.
2. Things that are not errors (and will deploy successfully), but might be
sub-optimal configuration. For example, a warning is used when an Serverless
V2 Aurora cluster is created and no reader instances are created in tiers
0-1. This is a valid configuration, but could cause availability issues in
certain scenarios.

Users who are developing CDK applications may want to always use the `--strict`
CLI argument to turn all warnings into errors. Currently this is probably not
possible since as soon as one warning is not applicable they can no longer use
`--strict`. Ideally they should be able to `acknowledge` warnings (similar to
what they can do with notices) indicating that the warning is not applicable to
them/they acknowledge the risk.

```ts
Annotations.of(this).acknowledgeWarning(
'@aws-cdk/aws-iam:maxPoliciesExceeded',
'A limit increase has been submitted',
);
```

This would allow acknowledgements to live alongside the code so all developers
on the code base would have the information readily available. This also allows
the acknowledgement to be added at a certain `scope` and apply to all child
constructs. Users would be able to acknowledge certain warnings for the entire
app, or for a specific scope.

## Constraints

Warnings are currently added with the `Annotations` API.

```ts
Annotations.of(scope).addWarning('This is a warning');
```

`Annotations.of(scope)` creates a new instance of `Annotations` every time so
there is no way to keep track of warnings added. For example, doing something
like this would not work because the `Annotations` instance created with
`acknowledgeWarning` would be different than the one created with `addWarning`.

```ts
Annotations.of(scope).addWarning('This is a warning');
Annotations.of(scope).acknowledgeWarning('This is a warning');
```

Currently the storage mechanism for `Annotations` is the `Node` metadata. The
warning is added to the node as node metadata which is read by the CLI after
synthesis to print out the messages. This means that Annotations can be added
during `synthesis`.

Another constraint is that currently you add a warning with only the message.
There is no unique identifier. This means that to add an acknowledgement the
user would need to copy the entire message.

## Decision

We will deprecate the `addWarning` method and add a new method `addWarningV2`

```ts
/**
* @param id the unique identifier for the warning. This can be used to acknowledge the warning
* @param message The warning message.
*/
public addWarningV2(id: string, message: string): void
```

We will add a new method `acknowledgeWarning` that will allow users to
acknowledge a specific warning by `id`.

```ts
/**
* @param id - the id of the warning message to acknowledge
* @param message optional message to explain the reason for acknowledgement
*/
public acknowledgeWarning(id: string, message?: string): void
```

We will continue to use the node metadata as the storage mechanism and will add
a new metadata type `aws:cdk:acknowledge` to store information on
acknowledgements.

At synthesis when we collect all of the annotation messages, we will filter out
any messages that have an acknowledgement.

Storing the acknowledgements in the metadata will also allow us to report on
warnings that were acknowledged by the user (info will be stored in the
assembly).

## Alternatives

### Alternative storage mechanism

One alternative that was considered was to implement some alternative
intermediary storage mechanism. This storage mechanism would allow storing all
warnings and acknowledgements in a special construct that was created once per
app. It would look something like this (pseudo code)

```ts
class AnnotationManager extends Construct {
private constructor(scope: Construct) {
attachCustomSynthesis(this, {
onSynthesize: () => {
this.warnings.forEach(warning => node.addMetadata(...))
}
}
}

public addWarning() {
if (!this.acks.has()) {
this.warnings.set();
}
}
public ack() {
if (this.warnings.has()) {
this.warnings.delete();
}
this.acks.add();
}
}
```

The problem with this method is represented by the `attachCustomSynthesis` in
the example. This same applies for if we used `addValidation` or `Aspects`.
Annotations can be added _after_ that which means they would not be added or
acknowledged.

### Use context and remove metadata

Another alternative that was considered was to use context to set
acknowledgements. This would look something like this:

```ts
public acknowledgeWarning(id: string, message?: string) {
this.scope.node.setContext(id, message ?? true); // can't do this today
this.scope.node.removeMetadata(id); // this method does not exist
}
public addWarningV2(id: string, message: string) {
if (this.scope.node.tryGetContext(id) === undefined) {
this.addMessage(...);
}
}
```

There are two issues with this alternative.

1. It is currently not possible to `node.setContext` once children are added. We
would have to first remove that limitation. I think this could lead to a lot
of issues where users have to keep track of _when_ context is added.
2. We would have to implement the ability to `removeMetadata` from a node. This
functionality doesn't make much sense outside of this context (can't find
where anybody has asked for it). It also may require updating the metadata
types since currently there is no unique identifier for a given metadata
entry (other than the message).

## Consequences

With the recommended solution the only major consequence is that it requires
updating the `metadata-schema`, but we can do this in a non-breaking way
(addition of new types). The alternatives may also require changes to the schema
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changes the schema, and interpreting the schema becomes more complex. I would really really like to avoid having an add list and a scrap list in the same piece of data, that the client hen has to evaluate.

Adding this as a feature to the core seems preferable to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rix0rrr Then do you think the context + remove metadata option is the one to go with?

as well.