Skip to content

Commit

Permalink
Merge branch 'aws:main' into SupportIOOptimized
Browse files Browse the repository at this point in the history
  • Loading branch information
Zishanwang1992 authored May 25, 2023
2 parents f241877 + bb7f4f4 commit eaf6f8a
Show file tree
Hide file tree
Showing 19 changed files with 524 additions and 115 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
"fs-extra": "^9.1.0",
"graceful-fs": "^4.2.11",
"jest-junit": "^13.2.0",
"jsii-diff": "1.81.0",
"jsii-pacmak": "1.81.0",
"jsii-reflect": "1.81.0",
"jsii-diff": "1.82.0",
"jsii-pacmak": "1.82.0",
"jsii-reflect": "1.82.0",
"jsii-rosetta": "~5.0.8",
"lerna": "^6.6.1",
"nx": "^15.9.4",
Expand Down
86 changes: 43 additions & 43 deletions packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions packages/aws-cdk-lib/aws-lambda/lib/function-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ export class FunctionUrl extends Resource implements IFunctionUrl {
}

private renderCors(cors: FunctionUrlCorsOptions): CfnUrl.CorsProperty {
if (cors.maxAge && !cors.maxAge.isUnresolved() && cors.maxAge.toSeconds() > 86400) {
throw new Error(`FunctionUrl CORS maxAge should be less than or equal to 86400 secs (got ${cors.maxAge.toSeconds()})`);
}

return {
allowCredentials: cors.allowCredentials,
allowHeaders: cors.allowedHeaders,
Expand Down
20 changes: 20 additions & 0 deletions packages/aws-cdk-lib/aws-lambda/test/function-url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,26 @@ describe('FunctionUrl', () => {
}).toThrow(/FunctionUrl cannot be used with a Version/);
});

test('throws when CORS maxAge is greater than 86400 secs', () => {
// GIVEN
const stack = new cdk.Stack();
const fn = new lambda.Function(stack, 'MyLambda', {
code: new lambda.InlineCode('hello()'),
handler: 'index.hello',
runtime: lambda.Runtime.NODEJS_14_X,
});

// WHEN
expect(() => {
new lambda.FunctionUrl(stack, 'FunctionUrl', {
function: fn,
cors: {
maxAge: cdk.Duration.seconds(86401),
},
});
}).toThrow(/FunctionUrl CORS maxAge should be less than or equal to 86400 secs/);
});

test('grantInvokeUrl: adds appropriate permissions', () => {
// GIVEN
const stack = new cdk.Stack();
Expand Down
9 changes: 7 additions & 2 deletions packages/aws-cdk-lib/aws-s3-deployment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,19 @@ declare const destinationBucket: s3.Bucket;
new s3deploy.BucketDeployment(this, 'BucketDeployment', {
sources: [s3deploy.Source.asset('./website', { exclude: ['index.html'] })],
destinationBucket,
cacheControl: [s3deploy.CacheControl.fromString('max-age=31536000,public,immutable')],
cacheControl: [
s3deploy.CacheControl.maxAge(Duration.days(365)),
s3deploy.CacheControl.immutable(),
],
prune: false,
});

new s3deploy.BucketDeployment(this, 'HTMLBucketDeployment', {
sources: [s3deploy.Source.asset('./website', { exclude: ['*', '!index.html'] })],
destinationBucket,
cacheControl: [s3deploy.CacheControl.fromString('max-age=0,no-cache,no-store,must-revalidate')],
cacheControl: [
s3deploy.CacheControl.maxAge(Duration.seconds(0)),
],
prune: false,
});
```
Expand Down
25 changes: 25 additions & 0 deletions packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,16 @@ export class CacheControl {
*/
public static noTransform() { return new CacheControl('no-transform'); }

/**
* Sets 'no-store'.
*/
public static noStore() { return new CacheControl('no-store'); }

/**
* Sets 'must-understand'.
*/
public static mustUnderstand() { return new CacheControl('must-understand'); }

/**
* Sets 'public'.
*/
Expand All @@ -641,6 +651,11 @@ export class CacheControl {
*/
public static setPrivate() { return new CacheControl('private'); }

/**
* Sets 'immutable'.
*/
public static immutable() { return new CacheControl('immutable'); }

/**
* Sets 'proxy-revalidate'.
*/
Expand All @@ -656,6 +671,16 @@ export class CacheControl {
*/
public static sMaxAge(t: cdk.Duration) { return new CacheControl(`s-maxage=${t.toSeconds()}`); }

/**
* Sets 'stale-while-revalidate=<duration-in-seconds>'.
*/
public static staleWhileRevalidate(t: cdk.Duration) { return new CacheControl(`stale-while-revalidate=${t.toSeconds()}`); }

/**
* Sets 'stale-if-error=<duration-in-seconds>'.
*/
public static staleIfError(t: cdk.Duration) { return new CacheControl(`stale-if-error=${t.toSeconds()}`); }

/**
* Constructs a custom cache control key from the literal value.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,11 +514,16 @@ test('cache control type has correct values', () => {
expect(s3deploy.CacheControl.mustRevalidate().value).toEqual('must-revalidate');
expect(s3deploy.CacheControl.noCache().value).toEqual('no-cache');
expect(s3deploy.CacheControl.noTransform().value).toEqual('no-transform');
expect(s3deploy.CacheControl.noStore().value).toEqual('no-store');
expect(s3deploy.CacheControl.mustUnderstand().value).toEqual('must-understand');
expect(s3deploy.CacheControl.setPublic().value).toEqual('public');
expect(s3deploy.CacheControl.setPrivate().value).toEqual('private');
expect(s3deploy.CacheControl.immutable().value).toEqual('immutable');
expect(s3deploy.CacheControl.proxyRevalidate().value).toEqual('proxy-revalidate');
expect(s3deploy.CacheControl.maxAge(cdk.Duration.minutes(1)).value).toEqual('max-age=60');
expect(s3deploy.CacheControl.sMaxAge(cdk.Duration.minutes(1)).value).toEqual('s-maxage=60');
expect(s3deploy.CacheControl.staleWhileRevalidate(cdk.Duration.minutes(1)).value).toEqual('stale-while-revalidate=60');
expect(s3deploy.CacheControl.staleIfError(cdk.Duration.minutes(1)).value).toEqual('stale-if-error=60');
expect(s3deploy.CacheControl.fromString('only-if-cached').value).toEqual('only-if-cached');
});

Expand Down
7 changes: 5 additions & 2 deletions packages/aws-cdk-lib/core/lib/cfn-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,12 @@ export class CfnResource extends CfnRefElement {

protected get cfnProperties(): { [key: string]: any } {
const props = this._cfnProperties || {};
if (TagManager.isTaggable(this)) {
const tagMgr = TagManager.of(this);
if (tagMgr) {
const tagsProp: { [key: string]: any } = {};
tagsProp[this.tags.tagPropertyName] = this.tags.renderTags();
// If this object has a TagManager, then render it out into the correct field. We assume there
// is no shadow tags object, so we don't pass anything to renderTags().
tagsProp[tagMgr.tagPropertyName] = tagMgr.renderTags();
return deepMerge(props, tagsProp);
}
return props;
Expand Down
31 changes: 25 additions & 6 deletions packages/aws-cdk-lib/core/lib/tag-aspect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Construct, IConstruct } from 'constructs';
import { Annotations } from './annotations';
import { IAspect, Aspects } from './aspect';
import { ITaggable, TagManager } from './tag-manager';
import { ITaggable, ITaggableV2, TagManager } from './tag-manager';

/**
* Properties for a tag
Expand Down Expand Up @@ -72,12 +72,15 @@ abstract class TagBase implements IAspect {
}

public visit(construct: IConstruct): void {
if (TagManager.isTaggable(construct)) {
if (TagManager.isTaggableV2(construct)) {
this.applyTagV2(construct);
} else if (TagManager.isTaggable(construct)) {
this.applyTag(construct);
}
}

protected abstract applyTag(resource: ITaggable): void;
protected abstract applyTagV2(resource: ITaggableV2): void;
}

/**
Expand Down Expand Up @@ -121,8 +124,16 @@ export class Tag extends TagBase {
}

protected applyTag(resource: ITaggable) {
if (resource.tags.applyTagAspectHere(this.props.includeResourceTypes, this.props.excludeResourceTypes)) {
resource.tags.setTag(
this.applyManager(resource.tags);
}

protected applyTagV2(resource: ITaggableV2) {
this.applyManager(resource.cdkTagManager);
}

private applyManager(mgr: TagManager) {
if (mgr.applyTagAspectHere(this.props.includeResourceTypes, this.props.excludeResourceTypes)) {
mgr.setTag(
this.key,
this.value,
this.props.priority ?? this.defaultPriority,
Expand Down Expand Up @@ -173,8 +184,16 @@ export class RemoveTag extends TagBase {
}

protected applyTag(resource: ITaggable): void {
if (resource.tags.applyTagAspectHere(this.props.includeResourceTypes, this.props.excludeResourceTypes)) {
resource.tags.removeTag(this.key, this.props.priority ?? this.defaultPriority);
this.applyManager(resource.tags);
}

protected applyTagV2(resource: ITaggableV2): void {
this.applyManager(resource.cdkTagManager);
}

private applyManager(mgr: TagManager) {
if (mgr.applyTagAspectHere(this.props.includeResourceTypes, this.props.excludeResourceTypes)) {
mgr.removeTag(this.key, this.props.priority ?? this.defaultPriority);
}
}
}
68 changes: 58 additions & 10 deletions packages/aws-cdk-lib/core/lib/tag-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,23 @@ export interface ITaggable {
readonly tags: TagManager;
}

/**
* Modernized version of ITaggable
*
* `ITaggable` has a problem: for a number of L1 resources, we failed to generate
* `tags: TagManager`, and generated `tags: CfnSomeResource.TagProperty[]` instead.
*
* To mark these resources as taggable, we need to put the `TagManager` in a new property
* whose name is unlikely to conflict with any existing properties. Hence, a new interface
* for that purpose. All future resources will implement `ITaggableV2`.
*/
export interface ITaggableV2 {
/**
* TagManager to set, remove and format tags
*/
readonly cdkTagManager: TagManager;
}

/**
* Options to configure TagManager behavior
*/
Expand Down Expand Up @@ -283,7 +300,22 @@ export class TagManager {
* Check whether the given construct is Taggable
*/
public static isTaggable(construct: any): construct is ITaggable {
return (construct as any).tags !== undefined;
const tags = (construct as any).tags;
return tags && typeof tags === 'object' && tags.constructor.name === 'TagManager';
}

/**
* Check whether the given construct is ITaggableV2
*/
public static isTaggableV2(construct: any): construct is ITaggableV2 {
return (construct as any).cdkTagManager !== undefined;
}

/**
* Return the TagManager associated with the given construct, if any
*/
public static of(construct: any): TagManager | undefined {
return TagManager.isTaggableV2(construct) ? construct.cdkTagManager : TagManager.isTaggable(construct) ? construct.tags : undefined;
}

/**
Expand All @@ -303,21 +335,19 @@ export class TagManager {
public readonly renderedTags: IResolvable;

private readonly tags = new Map<string, Tag>();
private readonly dynamicTags: any;
private dynamicTags?: any;
private readonly priorities = new Map<string, number>();
private readonly tagFormatter: ITagFormatter;
private readonly resourceTypeName: string;
private readonly initialTagPriority = 50;
private readonly externalTagPriority = 50;
private readonly didHaveInitialTags: boolean;

constructor(tagType: TagType, resourceTypeName: string, tagStructure?: any, options: TagManagerOptions = { }) {
constructor(tagType: TagType, resourceTypeName: string, initialTags?: any, options: TagManagerOptions = { }) {
this.resourceTypeName = resourceTypeName;
this.tagFormatter = TAG_FORMATTERS()[tagType];
if (tagStructure !== undefined) {
const parseTagsResult = this.tagFormatter.parseTags(tagStructure, this.initialTagPriority);
this.dynamicTags = parseTagsResult.dynamicTags;
this._setTag(...parseTagsResult.tags);
}
this.tagPropertyName = options.tagPropertyName || 'tags';
this.parseExternalTags(initialTags);
this.didHaveInitialTags = initialTags !== undefined;

this.renderedTags = Lazy.any({ produce: () => this.renderTags() });
}
Expand Down Expand Up @@ -353,8 +383,13 @@ export class TagManager {
* which will return a `Lazy` value that will resolve to the correct
* tags at synthesis time.
*/
public renderTags(): any {
public renderTags(combineWithTags?: any): any {
if (combineWithTags !== undefined && this.didHaveInitialTags) {
throw new Error('Specify external tags either during the creation of TagManager, or as a parameter to renderTags(), but not both');
}
this.parseExternalTags(combineWithTags);
const formattedTags = this.tagFormatter.formatTags(this.sortedTags);

if (Array.isArray(formattedTags) || Array.isArray(this.dynamicTags)) {
const ret = [...formattedTags ?? [], ...this.dynamicTags ?? []];
return ret.length > 0 ? ret : undefined;
Expand Down Expand Up @@ -412,4 +447,17 @@ export class TagManager {
return Array.from(this.tags.values())
.sort((a, b) => a.key.localeCompare(b.key));
}

/**
* Parse external tags.
*
* Set the parseable ones into this tag manager. Save the rest (tokens, lazies) in `this.dynamicTags`.
*/
private parseExternalTags(initialTags: any) {
if (initialTags !== undefined) {
const parseTagsResult = this.tagFormatter.parseTags(initialTags, this.externalTagPriority);
this.dynamicTags = parseTagsResult.dynamicTags;
this._setTag(...parseTagsResult.tags);
}
}
}
Loading

0 comments on commit eaf6f8a

Please sign in to comment.