From 91680a5e9f65f6dfa785a3d7adfddfae76bf96cf Mon Sep 17 00:00:00 2001 From: Trevor Burnham Date: Sat, 21 Dec 2024 08:31:01 -0500 Subject: [PATCH] fix(cloudwatch): Render region and accountId when directly set on metrics Closes #28731 --- ...hboardAndWidgetWithStartAndEnd.assets.json | 6 +- ...oardAndWidgetWithStartAndEnd.template.json | 6 +- .../cdk.out | 2 +- ...efaultTestDeployAssert4D8483F4.assets.json | 2 +- .../integ.json | 2 +- .../manifest.json | 6 +- .../tree.json | 10 +-- ...dashboard-and-widget-with-start-and-end.ts | 2 + .../aws-cloudwatch/lib/metric-types.ts | 14 ++++ .../aws-cdk-lib/aws-cloudwatch/lib/metric.ts | 66 +++++++++++++++---- .../aws-cloudwatch/lib/private/rendering.ts | 12 +++- .../test/cross-environment.test.ts | 13 ++++ 12 files changed, 111 insertions(+), 30 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.assets.json index 048b1a92a7635..0716094f8358f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.assets.json @@ -1,7 +1,7 @@ { - "version": "34.0.0", + "version": "38.0.1", "files": { - "874df94f43f12341a3001f4b19d4e1bba754a4fc3a33c6a592ae6c265fc99a44": { + "ffda0354b028815a1953777c34e49bbf4ec8eb0eb04ccd47e60eff77dbcefe33": { "source": { "path": "DashboardAndWidgetWithStartAndEnd.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "874df94f43f12341a3001f4b19d4e1bba754a4fc3a33c6a592ae6c265fc99a44.json", + "objectKey": "ffda0354b028815a1953777c34e49bbf4ec8eb0eb04ccd47e60eff77dbcefe33.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.template.json index 9ff3f1cedf6cc..8f7e608fdb1e3 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/DashboardAndWidgetWithStartAndEnd.template.json @@ -11,15 +11,15 @@ { "Ref": "AWS::Region" }, - "\",\"metrics\":[[\"CDK/Test\",\"Metric\"]],\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":3,\"properties\":{\"view\":\"timeSeries\",\"region\":\"", + "\",\"metrics\":[[\"CDK/Test\",\"Metric\",{\"accountId\":\"1234\",\"region\":\"us-north-5\"}]],\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":3,\"properties\":{\"view\":\"timeSeries\",\"region\":\"", { "Ref": "AWS::Region" }, - "\",\"metrics\":[[\"CDK/Test\",\"Metric\"]],\"yAxis\":{},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":9,\"properties\":{\"view\":\"gauge\",\"region\":\"", + "\",\"metrics\":[[\"CDK/Test\",\"Metric\",{\"accountId\":\"1234\",\"region\":\"us-north-5\"}]],\"yAxis\":{},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":9,\"properties\":{\"view\":\"gauge\",\"region\":\"", { "Ref": "AWS::Region" }, - "\",\"metrics\":[[\"CDK/Test\",\"Metric\"]],\"yAxis\":{\"left\":{\"min\":0,\"max\":100}},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}}]}" + "\",\"metrics\":[[\"CDK/Test\",\"Metric\",{\"accountId\":\"1234\",\"region\":\"us-north-5\"}]],\"yAxis\":{\"left\":{\"min\":0,\"max\":100}},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}}]}" ] ] } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdk.out index 2313ab5436501..c6e612584e352 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdkintegdashboardandwidgetwithstartandendDefaultTestDeployAssert4D8483F4.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdkintegdashboardandwidgetwithstartandendDefaultTestDeployAssert4D8483F4.assets.json index eff956f039962..382c5bbbce8d5 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdkintegdashboardandwidgetwithstartandendDefaultTestDeployAssert4D8483F4.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/cdkintegdashboardandwidgetwithstartandendDefaultTestDeployAssert4D8483F4.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "38.0.1", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/integ.json index 0ef12514015b1..f68abd5b184d3 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "38.0.1", "testCases": { "cdk-integ-dashboard-and-widget-with-start-and-end/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/manifest.json index da40c03020eed..2a45f59d0938e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "38.0.1", "artifacts": { "DashboardAndWidgetWithStartAndEnd.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "DashboardAndWidgetWithStartAndEnd.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/874df94f43f12341a3001f4b19d4e1bba754a4fc3a33c6a592ae6c265fc99a44.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ffda0354b028815a1953777c34e49bbf4ec8eb0eb04ccd47e60eff77dbcefe33.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -67,6 +68,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "cdkintegdashboardandwidgetwithstartandendDefaultTestDeployAssert4D8483F4.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/tree.json index dec19d3db0811..397fccf676f79 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.js.snapshot/tree.json @@ -26,15 +26,15 @@ { "Ref": "AWS::Region" }, - "\",\"metrics\":[[\"CDK/Test\",\"Metric\"]],\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":3,\"properties\":{\"view\":\"timeSeries\",\"region\":\"", + "\",\"metrics\":[[\"CDK/Test\",\"Metric\",{\"accountId\":\"1234\",\"region\":\"us-north-5\"}]],\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":3,\"properties\":{\"view\":\"timeSeries\",\"region\":\"", { "Ref": "AWS::Region" }, - "\",\"metrics\":[[\"CDK/Test\",\"Metric\"]],\"yAxis\":{},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":9,\"properties\":{\"view\":\"gauge\",\"region\":\"", + "\",\"metrics\":[[\"CDK/Test\",\"Metric\",{\"accountId\":\"1234\",\"region\":\"us-north-5\"}]],\"yAxis\":{},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":9,\"properties\":{\"view\":\"gauge\",\"region\":\"", { "Ref": "AWS::Region" }, - "\",\"metrics\":[[\"CDK/Test\",\"Metric\"]],\"yAxis\":{\"left\":{\"min\":0,\"max\":100}},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}}]}" + "\",\"metrics\":[[\"CDK/Test\",\"Metric\",{\"accountId\":\"1234\",\"region\":\"us-north-5\"}]],\"yAxis\":{\"left\":{\"min\":0,\"max\":100}},\"start\":\"-P7D\",\"end\":\"2018-12-17T06:00:00.000Z\"}}]}" ] ] } @@ -86,7 +86,7 @@ "path": "cdk-integ-dashboard-and-widget-with-start-and-end/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.4.2" } }, "DeployAssert": { @@ -132,7 +132,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.4.2" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.ts index 280845d4e60ab..38e68985deb12 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.dashboard-and-widget-with-start-and-end.ts @@ -11,6 +11,8 @@ class TestStack extends Stack { const testMetric = new Metric({ namespace: 'CDK/Test', metricName: 'Metric', + account: '1234', + region: 'us-north-5', }); const singleValueWidget = new SingleValueWidget({ diff --git a/packages/aws-cdk-lib/aws-cloudwatch/lib/metric-types.ts b/packages/aws-cdk-lib/aws-cloudwatch/lib/metric-types.ts index 7b02013c48a27..46efb0bc56a87 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/lib/metric-types.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/lib/metric-types.ts @@ -323,6 +323,20 @@ export interface MetricStatConfig { * @default Deployment account. */ readonly account?: string; + + /** + * Region set directly on the metric, not inherited from the attached stack. + * + * @default No override. + */ + readonly regionOverride?: string; + + /** + * Account set directly on the metric, not inherited from the attached stack. + * + * @default No override. + */ + readonly accountOverride?: string; } /** diff --git a/packages/aws-cdk-lib/aws-cloudwatch/lib/metric.ts b/packages/aws-cdk-lib/aws-cloudwatch/lib/metric.ts index 428598e9602b5..6c03284987dfc 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/lib/metric.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/lib/metric.ts @@ -115,6 +115,20 @@ export interface CommonMetricOptions { * @default - Deployment region. */ readonly region?: string; + + /** + * Account of the stack this metric is attached to. + * + * @default - Deployment account. + */ + readonly stackAccount?: string; + + /** + * Region of the stack this metric is attached to. + * + * @default - Deployment region. + */ + readonly stackRegion?: string; } /** @@ -306,11 +320,17 @@ export class Metric implements IMetric { /** Unit of the metric. */ public readonly unit?: Unit; - /** Account which this metric comes from */ - public readonly account?: string; + /** Account of the stack this metric is attached to. */ + private readonly stackAccount?: string; + + /** Region of the stack this metric is attached to. */ + private readonly stackRegion?: string; - /** Region which this metric comes from. */ - public readonly region?: string; + /** Account set directly on the metric, taking precedence over the stack account. */ + private readonly accountOverride?: string; + + /** Region set directly on the metric, taking precedence over the stack region. */ + private readonly regionOverride?: string; /** * Warnings attached to this metric. @@ -352,8 +372,10 @@ export class Metric implements IMetric { this.label = props.label; this.color = props.color; this.unit = props.unit; - this.account = props.account; - this.region = props.region; + this.stackAccount = props.stackAccount; + this.stackRegion = props.stackRegion; + this.accountOverride = props.account; + this.regionOverride = props.region; } /** @@ -369,8 +391,10 @@ export class Metric implements IMetric { && (props.color === undefined || props.color === this.color) && (props.statistic === undefined || props.statistic === this.statistic) && (props.unit === undefined || props.unit === this.unit) - && (props.account === undefined || props.account === this.account) - && (props.region === undefined || props.region === this.region) + && (props.account === undefined || props.account === this.accountOverride) + && (props.region === undefined || props.region === this.regionOverride) + && (props.stackAccount === undefined || props.stackAccount === this.stackAccount) + && (props.stackRegion === undefined || props.stackRegion === this.stackRegion) // For these we're not going to do deep equality, misses some opportunity for optimization // but that's okay. && (props.dimensions === undefined) @@ -388,8 +412,10 @@ export class Metric implements IMetric { unit: ifUndefined(props.unit, this.unit), label: ifUndefined(props.label, this.label), color: ifUndefined(props.color, this.color), - account: ifUndefined(props.account, this.account), - region: ifUndefined(props.region, this.region), + account: ifUndefined(props.account, this.accountOverride), + region: ifUndefined(props.region, this.regionOverride), + stackAccount: ifUndefined(props.stackAccount, this.stackAccount), + stackRegion: ifUndefined(props.stackRegion, this.stackRegion), }); } @@ -409,11 +435,25 @@ export class Metric implements IMetric { const stack = cdk.Stack.of(scope); return this.with({ - region: cdk.Token.isUnresolved(stack.region) ? undefined : stack.region, - account: cdk.Token.isUnresolved(stack.account) ? undefined : stack.account, + stackAccount: cdk.Token.isUnresolved(stack.account) ? undefined : stack.account, + stackRegion: cdk.Token.isUnresolved(stack.region) ? undefined : stack.region, }); } + /** + * Account which this metric comes from. + */ + public get account(): string | undefined { + return this.accountOverride || this.stackAccount; + } + + /** + * Region which this metric comes from. + */ + public get region(): string | undefined { + return this.regionOverride || this.stackRegion; + } + public toMetricConfig(): MetricConfig { const dims = this.dimensionsAsList(); return { @@ -426,6 +466,8 @@ export class Metric implements IMetric { unitFilter: this.unit, account: this.account, region: this.region, + accountOverride: this.accountOverride, + regionOverride: this.regionOverride, }, renderingProperties: { color: this.color, diff --git a/packages/aws-cdk-lib/aws-cloudwatch/lib/private/rendering.ts b/packages/aws-cdk-lib/aws-cloudwatch/lib/private/rendering.ts index 1aff96bbd120b..db815e42986c4 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/lib/private/rendering.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/lib/private/rendering.ts @@ -47,8 +47,16 @@ function metricGraphJson(metric: IMetric, yAxis?: string, id?: string) { } // Metric attributes that are rendered to graph options - if (stat.account) { options.accountId = accountIfDifferentFromStack(stat.account); } - if (stat.region) { options.region = regionIfDifferentFromStack(stat.region); } + if (stat.accountOverride) { + options.accountId = stat.accountOverride; + } else if (stat.account) { + options.accountId = accountIfDifferentFromStack(stat.account); + } + if (stat.regionOverride) { + options.region = stat.regionOverride; + } else if (stat.region) { + options.region = regionIfDifferentFromStack(stat.region); + } if (stat.period && stat.period.toSeconds() !== 300) { options.period = stat.period.toSeconds(); } if (stat.statistic && stat.statistic !== 'Average') { options.stat = stat.statistic; } }, diff --git a/packages/aws-cdk-lib/aws-cloudwatch/test/cross-environment.test.ts b/packages/aws-cdk-lib/aws-cloudwatch/test/cross-environment.test.ts index 14a68d64355fd..ece55e300cf21 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/test/cross-environment.test.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/test/cross-environment.test.ts @@ -59,7 +59,20 @@ describe('cross environment', () => { graphMetricsAre(new Stack(), graph, [ ['Test', 'ACount', { accountId: '1234', region: 'us-north-5' }], ]); + }); + test('metric with explicit account and region that match stack will render as-is', () => { + // GIVEN + const graph = new GraphWidget({ + left: [ + a.with({ account: '1234', region: 'us-north-5' }), + ], + }); + + // THEN + graphMetricsAre(new Stack(undefined, undefined, { env: { region: 'us-north-5', account: '1234' } }), graph, [ + ['Test', 'ACount', { accountId: '1234', region: 'us-north-5' }], + ]); }); test('metric attached to agnostic stack will not render in agnostic stack', () => {