Skip to content

Commit

Permalink
Merge branch 'main' into fix-typos-lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Oct 21, 2024
2 parents ce35179 + 366b492 commit 7002583
Show file tree
Hide file tree
Showing 20 changed files with 2,795 additions and 21 deletions.
45 changes: 45 additions & 0 deletions packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,30 @@ export interface CdkModernBootstrapCommandOptions extends CommonCdkBootstrapComm
readonly usePreviousParameters?: boolean;
}

export interface CdkGarbageCollectionCommandOptions {
/**
* The amount of days an asset should stay isolated before deletion, to
* guard against some pipeline rollback scenarios
*
* @default 0
*/
readonly rollbackBufferDays?: number;

/**
* The type of asset that is getting garbage collected.
*
* @default 'all'
*/
readonly type?: 'ecr' | 's3' | 'all';

/**
* The name of the bootstrap stack
*
* @default 'CdkToolkit'
*/
readonly bootstrapStackName?: string;
}

export class TestFixture extends ShellHelper {
public readonly qualifier = this.randomString.slice(0, 10);
private readonly bucketsToDelete = new Array<string>();
Expand Down Expand Up @@ -464,6 +488,26 @@ export class TestFixture extends ShellHelper {
});
}

public async cdkGarbageCollect(options: CdkGarbageCollectionCommandOptions): Promise<string> {
const args = [
'gc',
'--unstable=gc', // TODO: remove when stabilizing
'--confirm=false',
'--created-buffer-days=0', // Otherwise all assets created during integ tests are too young
];
if (options.rollbackBufferDays) {
args.push('--rollback-buffer-days', String(options.rollbackBufferDays));
}
if (options.type) {
args.push('--type', options.type);
}
if (options.bootstrapStackName) {
args.push('--bootstrapStackName', options.bootstrapStackName);
}

return this.cdk(args);
}

public async cdkMigrate(language: string, stackName: string, inputPath?: string, options?: CdkCliOptions) {
return this.cdk([
'migrate',
Expand Down Expand Up @@ -634,6 +678,7 @@ async function ensureBootstrapped(fixture: TestFixture) {
CDK_NEW_BOOTSTRAP: '1',
},
});

ALREADY_BOOTSTRAPPED_IN_THIS_RUN.add(envSpecifier);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { GetObjectTaggingCommand, ListObjectsV2Command, PutObjectTaggingCommand } from '@aws-sdk/client-s3';
import { integTest, randomString, withoutBootstrap } from '../../lib';

jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime

integTest(
'Garbage Collection deletes unused assets',
withoutBootstrap(async (fixture) => {
const toolkitStackName = fixture.bootstrapStackName;
const bootstrapBucketName = `aws-cdk-garbage-collect-integ-test-bckt-${randomString()}`;
fixture.rememberToDeleteBucket(bootstrapBucketName); // just in case

await fixture.cdkBootstrapModern({
toolkitStackName,
bootstrapBucketName,
});

await fixture.cdkDeploy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});
fixture.log('Setup complete!');

await fixture.cdkDestroy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});

await fixture.cdkGarbageCollect({
rollbackBufferDays: 0,
type: 's3',
bootstrapStackName: toolkitStackName,
});
fixture.log('Garbage collection complete!');

// assert that the bootstrap bucket is empty
await fixture.aws.s3.send(new ListObjectsV2Command({ Bucket: bootstrapBucketName }))
.then((result) => {
expect(result.Contents).toBeUndefined();
});
}),
);

integTest(
'Garbage Collection keeps in use assets',
withoutBootstrap(async (fixture) => {
const toolkitStackName = fixture.bootstrapStackName;
const bootstrapBucketName = `aws-cdk-garbage-collect-integ-test-bckt-${randomString()}`;
fixture.rememberToDeleteBucket(bootstrapBucketName); // just in case

await fixture.cdkBootstrapModern({
toolkitStackName,
bootstrapBucketName,
});

await fixture.cdkDeploy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});
fixture.log('Setup complete!');

await fixture.cdkGarbageCollect({
rollbackBufferDays: 0,
type: 's3',
bootstrapStackName: toolkitStackName,
});
fixture.log('Garbage collection complete!');

// assert that the bootstrap bucket has the object
await fixture.aws.s3.send(new ListObjectsV2Command({ Bucket: bootstrapBucketName }))
.then((result) => {
expect(result.Contents).toHaveLength(1);
});

await fixture.cdkDestroy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});
fixture.log('Teardown complete!');
}),
);

integTest(
'Garbage Collection tags unused assets',
withoutBootstrap(async (fixture) => {
const toolkitStackName = fixture.bootstrapStackName;
const bootstrapBucketName = `aws-cdk-garbage-collect-integ-test-bckt-${randomString()}`;
fixture.rememberToDeleteBucket(bootstrapBucketName); // just in case

await fixture.cdkBootstrapModern({
toolkitStackName,
bootstrapBucketName,
});

await fixture.cdkDeploy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});
fixture.log('Setup complete!');

await fixture.cdkDestroy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});

await fixture.cdkGarbageCollect({
rollbackBufferDays: 100, // this will ensure that we do not delete assets immediately (and just tag them)
type: 's3',
bootstrapStackName: toolkitStackName,
});
fixture.log('Garbage collection complete!');

// assert that the bootstrap bucket has the object and is tagged
await fixture.aws.s3.send(new ListObjectsV2Command({ Bucket: bootstrapBucketName }))
.then(async (result) => {
expect(result.Contents).toHaveLength(2); // also the CFN template
const key = result.Contents![0].Key;
const tags = await fixture.aws.s3.send(new GetObjectTaggingCommand({ Bucket: bootstrapBucketName, Key: key }));
expect(tags.TagSet).toHaveLength(1);
});
}),
);

integTest(
'Garbage Collection untags in-use assets',
withoutBootstrap(async (fixture) => {
const toolkitStackName = fixture.bootstrapStackName;
const bootstrapBucketName = `aws-cdk-garbage-collect-integ-test-bckt-${randomString()}`;
fixture.rememberToDeleteBucket(bootstrapBucketName); // just in case

await fixture.cdkBootstrapModern({
toolkitStackName,
bootstrapBucketName,
});

await fixture.cdkDeploy('lambda', {
options: [
'--context', `bootstrapBucket=${bootstrapBucketName}`,
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--toolkit-stack-name', toolkitStackName,
'--force',
],
});
fixture.log('Setup complete!');

// Artificially add tagging to the asset in the bootstrap bucket
const result = await fixture.aws.s3.send(new ListObjectsV2Command({ Bucket: bootstrapBucketName }));
const key = result.Contents!.filter((c) => c.Key?.split('.')[1] == 'zip')[0].Key; // fancy footwork to make sure we have the asset key
await fixture.aws.s3.send(new PutObjectTaggingCommand({
Bucket: bootstrapBucketName,
Key: key,
Tagging: {
TagSet: [{
Key: 'aws-cdk:isolated',
Value: '12345',
}, {
Key: 'bogus',
Value: 'val',
}],
},
}));

await fixture.cdkGarbageCollect({
rollbackBufferDays: 100, // this will ensure that we do not delete assets immediately (and just tag them)
type: 's3',
bootstrapStackName: toolkitStackName,
});
fixture.log('Garbage collection complete!');

// assert that the isolated object tag is removed while the other tag remains
const newTags = await fixture.aws.s3.send(new GetObjectTaggingCommand({ Bucket: bootstrapBucketName, Key: key }));

expect(newTags.TagSet).toEqual([{
Key: 'bogus',
Value: 'val',
}]);
}),
);
37 changes: 37 additions & 0 deletions packages/@aws-cdk/aws-iot-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,40 @@ new iot.AccountAuditConfiguration(this, 'AuditConfiguration', {
},
});
```

### Scheduled Audit

You can create a [scheduled audit](https://docs.aws.amazon.com/iot-device-defender/latest/devguide/AuditCommands.html#device-defender-AuditCommandsManageSchedules) that is run at a specified time interval. Checks must be enabled for your account by creating `AccountAuditConfiguration`.

```ts
declare const config: iot.AccountAuditConfiguration;

// Daily audit
const dailyAudit = new iot.ScheduledAudit(this, 'DailyAudit', {
accountAuditConfiguration: config,
frequency: iot.Frequency.DAILY,
auditChecks: [
iot.AuditCheck.AUTHENTICATED_COGNITO_ROLE_OVERLY_PERMISSIVE_CHECK,
],
})

// Weekly audit
const weeklyAudit = new iot.ScheduledAudit(this, 'WeeklyAudit', {
accountAuditConfiguration: config,
frequency: iot.Frequency.WEEKLY,
dayOfWeek: iot.DayOfWeek.SUNDAY,
auditChecks: [
iot.AuditCheck.CA_CERTIFICATE_EXPIRING_CHECK,
],
});

// Monthly audit
const monthlyAudit = new iot.ScheduledAudit(this, 'MonthlyAudit', {
accountAuditConfiguration: config,
frequency: iot.Frequency.MONTHLY,
dayOfMonth: iot.DayOfMonth.of(1),
auditChecks: [
iot.AuditCheck.CA_CERTIFICATE_KEY_QUALITY_CHECK,
],
});
```
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-iot-alpha/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './action';
export * from './audit-configuration';
export * from './iot-sql';
export * from './logging';
export * from './scheduled-audit';
export * from './topic-rule';

// AWS::IoT CloudFormation Resources:
Loading

0 comments on commit 7002583

Please sign in to comment.