Skip to content

Commit

Permalink
fix(logs): Apply tags to log retention Lambda (aws#17029)
Browse files Browse the repository at this point in the history
This hopefully fixes aws#15032 by implementing the `ITaggable` interface on the custom construct and then applying discovered tags to the underlying `CfnResource`.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jamesorlakin authored and TikiTDO committed Feb 21, 2022
1 parent 6e141bf commit 0e79e33
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 3 deletions.
5 changes: 4 additions & 1 deletion packages/@aws-cdk/aws-logs/lib/log-retention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ export class LogRetention extends CoreConstruct {
/**
* Private provider Lambda function to support the log retention custom resource.
*/
class LogRetentionFunction extends CoreConstruct {
class LogRetentionFunction extends CoreConstruct implements cdk.ITaggable {
public readonly functionArn: cdk.Reference;

public readonly tags: cdk.TagManager = new cdk.TagManager(cdk.TagType.KEY_VALUE, 'AWS::Lambda::Function');

constructor(scope: Construct, id: string, props: LogRetentionProps) {
super(scope, id);

Expand Down Expand Up @@ -164,6 +166,7 @@ class LogRetentionFunction extends CoreConstruct {
S3Key: asset.s3ObjectKey,
},
Role: role.roleArn,
Tags: this.tags.renderedTags,
},
});
this.functionArn = resource.getAtt('Arn');
Expand Down
19 changes: 19 additions & 0 deletions packages/@aws-cdk/aws-logs/test/log-retention.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,23 @@ describe('log retention', () => {
expect(logGroupArn.endsWith(':*')).toEqual(true);

});

test('retention Lambda CfnResource receives propagated tags', () => {
const stack = new cdk.Stack();
cdk.Tags.of(stack).add('test-key', 'test-value');
new LogRetention(stack, 'MyLambda', {
logGroupName: 'group',
retention: RetentionDays.ONE_MONTH,
});

expect(stack).toHaveResourceLike('AWS::Lambda::Function', {
Tags: [
{
Key: 'test-key',
Value: 'test-value',
},
],
});

});
});
46 changes: 44 additions & 2 deletions packages/@aws-cdk/core/lib/tag-manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { TagType } from './cfn-resource';
import { CfnTag } from './cfn-tag';
import { Lazy } from './lazy';
import { IResolvable } from './resolvable';

interface Tag {
key: string;
Expand Down Expand Up @@ -172,7 +174,7 @@ class KeyValueFormatter implements ITagFormatter {
Value: tag.value,
});
});
return tags;
return tags.length > 0 ? tags : undefined;
}
}

Expand Down Expand Up @@ -228,7 +230,32 @@ export interface TagManagerOptions {
}

/**
* TagManager facilitates a common implementation of tagging for Constructs.
* TagManager facilitates a common implementation of tagging for Constructs
*
* Normally, you do not need to use this class, as the CloudFormation specification
* will indicate which resources are taggable. However, sometimes you will need this
* to make custom resources taggable. Used `tagManager.renderedTags` to obtain a
* value that will resolve to the tags at synthesis time.
*
* @example
* import * as cdk from '@aws-cdk/core';
*
* class MyConstruct extends cdk.Resource implements cdk.ITaggable {
* public readonly tags = new cdk.TagManager(cdk.TagType.KEY_VALUE, 'Whatever::The::Type');
*
* constructor(scope: cdk.Construct, id: string) {
* super(scope, id);
*
* new cdk.CfnResource(this, 'Resource', {
* type: 'Whatever::The::Type',
* properties: {
* // ...
* Tags: this.tags.renderedTags,
* },
* });
* }
* }
*
*/
export class TagManager {

Expand All @@ -247,6 +274,14 @@ export class TagManager {
*/
public readonly tagPropertyName: string;

/**
* A lazy value that represents the rendered tags at synthesis time
*
* If you need to make a custom construct taggable, use the value of this
* property to pass to the `tags` property of the underlying construct.
*/
public readonly renderedTags: IResolvable;

private readonly tags = new Map<string, Tag>();
private readonly priorities = new Map<string, number>();
private readonly tagFormatter: ITagFormatter;
Expand All @@ -260,6 +295,8 @@ export class TagManager {
this._setTag(...this.tagFormatter.parseTags(tagStructure, this.initialTagPriority));
}
this.tagPropertyName = options.tagPropertyName || 'tags';

this.renderedTags = Lazy.any({ produce: () => this.renderTags() });
}

/**
Expand Down Expand Up @@ -287,6 +324,11 @@ export class TagManager {

/**
* Renders tags into the proper format based on TagType
*
* This method will eagerly render the tags currently applied. In
* most cases, you should be using `tagManager.renderedTags` instead,
* which will return a `Lazy` value that will resolve to the correct
* tags at synthesis time.
*/
public renderTags(): any {
return this.tagFormatter.formatTags(this.sortedTags);
Expand Down

0 comments on commit 0e79e33

Please sign in to comment.