Skip to content

Commit b0e4a54

Browse files
authored
feat(cli): notices on bootstrap version (#31555)
### Reason for this change We would like to be able to send customers a notice when issues with bootstrap templates are discovered. ### Description of changes Currently, our notices mechanism can only match against CLI/Framework versions. In order to match against a bootstrap stack version, we need to hook into the deploy process, where we already perform bootstrap version checks. There were two options to implement the change: 1. Bubble up the bootstrap stack version all the up to the CLI entry-point, where notices are initialized. 2. Allow access to notices from anywhere in our CLI code base. I opted for number 2 because it is less disruptive (in terms of files changed) and more flexible for future code that might want to take advantage of the notices mechanism. The tricky thing is, notices are dependent on user configuration (i.e `Configuration`), which we don't have access to in this part of the code. To make it work, I created a new `Notices` singleton class. It is instantiated in the CLI entry-point (via `Notices.create` with user configuration), and can then be accessed from anywhere in the code (via `Notices.get()`). This change resulted in a pretty big refactor to the notices code, but keeps everything else untouched. ### Docs Documentation of enhanced notice authoring capabilities: cdklabs/aws-cdk-notices#631 ### Description of how you validated changes Added unit tests. ### Checklist - [X] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 5e08c98 commit b0e4a54

File tree

7 files changed

+1047
-516
lines changed

7 files changed

+1047
-516
lines changed

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts

+31
Original file line numberDiff line numberDiff line change
@@ -2259,3 +2259,34 @@ integTest(
22592259
expect(noticesUnacknowledged).toEqual(noticesUnacknowledgedAlias);
22602260
}),
22612261
);
2262+
2263+
integTest('cdk notices for bootstrap', withDefaultFixture(async (fixture) => {
2264+
2265+
const cache = {
2266+
expiration: 4125963264000, // year 2100 so we never overwrite the cache
2267+
notices: [{
2268+
title: 'Bootstrap stack outdated',
2269+
issueNumber: 16600,
2270+
overview: 'Your environments "{resolve:ENVIRONMENTS}" are running an outdated bootstrap stack.',
2271+
components: [{
2272+
name: 'bootstrap',
2273+
version: '<2000',
2274+
}],
2275+
schemaVersion: '1',
2276+
}],
2277+
};
2278+
2279+
const cdkCacheDir = path.join(fixture.integTestDir, 'cache');
2280+
await fs.mkdir(cdkCacheDir);
2281+
await fs.writeFile(path.join(cdkCacheDir, 'notices.json'), JSON.stringify(cache));
2282+
2283+
const output = await fixture.cdkDeploy('test-2', {
2284+
verbose: false,
2285+
modEnv: {
2286+
CDK_HOME: fixture.integTestDir,
2287+
},
2288+
});
2289+
2290+
expect(output).toContain('Your environments \"aws://');
2291+
2292+
}));

packages/aws-cdk/README.md

+21-15
Original file line numberDiff line numberDiff line change
@@ -879,33 +879,39 @@ $ cdk deploy
879879

880880
NOTICES
881881

882-
16603 Toggling off auto_delete_objects for Bucket empties the bucket
882+
22090 cli: cdk init produces EACCES: permission denied and does not fill the directory
883883

884-
Overview: If a stack is deployed with an S3 bucket with
885-
auto_delete_objects=True, and then re-deployed with
886-
auto_delete_objects=False, all the objects in the bucket
887-
will be deleted.
884+
Overview: The CLI is unable to initialize new apps if CDK is
885+
installed globally in a directory owned by root
888886

889-
Affected versions: <1.126.0.
887+
Affected versions: cli: 2.42.0.
890888

891-
More information at: https://github.com/aws/aws-cdk/issues/16603
889+
More information at: https://github.com/aws/aws-cdk/issues/22090
892890

893891

894-
17061 Error when building EKS cluster with monocdk import
892+
27547 Incorrect action in policy of Bucket `grantRead` method
895893

896-
Overview: When using monocdk/aws-eks to build a stack containing
897-
an EKS cluster, error is thrown about missing
898-
lambda-layer-node-proxy-agent/layer/package.json.
894+
Overview: Using the `grantRead` method on `aws-cdk-lib/aws-s3.Bucket`
895+
results in an invalid action attached to the resource policy
896+
which can cause unexpected failures when interacting
897+
with the bucket.
899898

900-
Affected versions: >=1.126.0 <=1.130.0.
899+
Affected versions: aws-cdk-lib.aws_s3.Bucket: 2.101.0.
901900

902-
More information at: https://github.com/aws/aws-cdk/issues/17061
901+
More information at: https://github.com/aws/aws-cdk/issues/27547
903902

904903

905-
If you don’t want to see an notice anymore, use "cdk acknowledge ID". For example, "cdk acknowledge 16603".
904+
If you don’t want to see a notice anymore, use "cdk acknowledge ID". For example, "cdk acknowledge 16603".
906905
```
907906

908-
You can suppress warnings in a variety of ways:
907+
There are several types of notices you may encounter, differentiated by the affected component:
908+
909+
- **cli**: notifies you about issues related to your CLI version.
910+
- **framework**: notifies you about issues related to your version of core constructs (e.g `Stack`).
911+
- **aws-cdk-lib.{module}.{construct}**: notifies you about issue related to your version of a specific construct (e.g `aws-cdk-lib.aws_s3.Bucket`)
912+
- **bootstrap**: notifies you about issues related to your version of the bootstrap stack. Note that these types of notices are only shown during the `cdk deploy` command.
913+
914+
You can suppress notices in a variety of ways:
909915

910916
- per individual execution:
911917

packages/aws-cdk/lib/api/environment-resources.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as cxapi from '@aws-cdk/cx-api';
22
import { ISDK } from './aws-auth';
33
import { EcrRepositoryInfo, ToolkitInfo } from './toolkit-info';
44
import { debug, warning } from '../logging';
5+
import { Notices } from '../notices';
56

67
/**
78
* Registry class for `EnvironmentResources`.
@@ -77,7 +78,7 @@ export class EnvironmentResources {
7778

7879
if (ssmParameterName !== undefined) {
7980
try {
80-
doValidate(await this.versionFromSsmParameter(ssmParameterName));
81+
doValidate(await this.versionFromSsmParameter(ssmParameterName), this.environment);
8182
return;
8283
} catch (e: any) {
8384
if (e.code !== 'AccessDeniedException') { throw e; }
@@ -92,7 +93,7 @@ export class EnvironmentResources {
9293
const bootstrapStack = await this.lookupToolkit();
9394
if (bootstrapStack.found && bootstrapStack.version < BOOTSTRAP_TEMPLATE_VERSION_INTRODUCING_GETPARAMETER) {
9495
warning(`Could not read SSM parameter ${ssmParameterName}: ${e.message}, falling back to version from ${bootstrapStack}`);
95-
doValidate(bootstrapStack.version);
96+
doValidate(bootstrapStack.version, this.environment);
9697
return;
9798
}
9899

@@ -102,9 +103,15 @@ export class EnvironmentResources {
102103

103104
// No SSM parameter
104105
const bootstrapStack = await this.lookupToolkit();
105-
doValidate(bootstrapStack.version);
106-
107-
function doValidate(version: number) {
106+
doValidate(bootstrapStack.version, this.environment);
107+
108+
function doValidate(version: number, environment: cxapi.Environment) {
109+
const notices = Notices.get();
110+
if (notices) {
111+
// if `Notices` hasn't been initialized there is probably a good
112+
// reason for it. handle gracefully.
113+
notices.addBootstrappedEnvironment({ bootstrapStackVersion: version, environment });
114+
}
108115
if (defExpectedVersion > version) {
109116
throw new Error(`This CDK deployment requires bootstrap stack version '${expectedVersion}', found '${version}'. Please run 'cdk bootstrap'.`);
110117
}

packages/aws-cdk/lib/cli.ts

+12-31
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { MIGRATE_SUPPORTED_LANGUAGES, getMigrateScanType } from '../lib/commands
2626
import { RequireApproval } from '../lib/diff';
2727
import { availableInitLanguages, cliInit, printAvailableTemplates } from '../lib/init';
2828
import { data, debug, error, print, setLogLevel, setCI } from '../lib/logging';
29-
import { displayNotices, refreshNotices } from '../lib/notices';
29+
import { Notices } from '../lib/notices';
3030
import { Command, Configuration, Settings } from '../lib/settings';
3131
import * as version from '../lib/version';
3232

@@ -367,10 +367,10 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
367367
});
368368
await configuration.load();
369369

370-
if (shouldDisplayNotices()) {
371-
void refreshNotices()
372-
.catch(e => debug(`Could not refresh notices: ${e}`));
373-
}
370+
const cmd = argv._[0];
371+
372+
const notices = Notices.create({ configuration, includeAcknowlegded: cmd === 'notices' ? !argv.unacknowledged : false });
373+
await notices.refresh();
374374

375375
const sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({
376376
profile: configuration.settings.get(['profile']),
@@ -422,8 +422,6 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
422422

423423
loadPlugins(configuration.settings);
424424

425-
const cmd = argv._[0];
426-
427425
if (typeof(cmd) !== 'string') {
428426
throw new Error(`First argument should be a string. Got: ${cmd} (${typeof(cmd)})`);
429427
}
@@ -440,32 +438,15 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
440438
// Do PSAs here
441439
await version.displayVersionMessage();
442440

443-
if (shouldDisplayNotices()) {
444-
if (cmd === 'notices' && argv.unacknowledged === true) {
445-
await displayNotices({
446-
outdir: configuration.settings.get(['output']) ?? 'cdk.out',
447-
acknowledgedIssueNumbers: configuration.context.get('acknowledged-issue-numbers') ?? [],
448-
ignoreCache: true,
449-
unacknowledged: true,
450-
});
451-
} else if (cmd === 'notices') {
452-
await displayNotices({
453-
outdir: configuration.settings.get(['output']) ?? 'cdk.out',
454-
acknowledgedIssueNumbers: [],
455-
ignoreCache: true,
456-
});
457-
} else if (cmd !== 'version') {
458-
await displayNotices({
459-
outdir: configuration.settings.get(['output']) ?? 'cdk.out',
460-
acknowledgedIssueNumbers: configuration.context.get('acknowledged-issue-numbers') ?? [],
461-
ignoreCache: false,
462-
});
463-
}
441+
if (cmd === 'notices') {
442+
await notices.refresh({ force: true });
443+
notices.display({ showTotal: argv.unacknowledged });
444+
445+
} else if (cmd !== 'version') {
446+
await notices.refresh();
447+
notices.display();
464448
}
465-
}
466449

467-
function shouldDisplayNotices(): boolean {
468-
return configuration.settings.get(['notices']) ?? true;
469450
}
470451

471452
async function main(command: string, args: any): Promise<number | void> {

0 commit comments

Comments
 (0)