From f241cac2dc6cdec545526d04b880dab36cf5df5a Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 9 Jul 2018 17:20:44 +0200 Subject: [PATCH] CloudWatch: add Metric.grantMetricPutData This adds a static helper function on `Metric` to grant metric writing permissions to IAM identities. Also move the `parseStatistics` function to a private source file so the unexported types it uses won't break the jsii build. Fixes #214. --- CHANGELOG.md | 4 ++ packages/@aws-cdk/cloudwatch/lib/alarm.ts | 3 +- packages/@aws-cdk/cloudwatch/lib/graph.ts | 3 +- packages/@aws-cdk/cloudwatch/lib/metric.ts | 66 +++++-------------- .../@aws-cdk/cloudwatch/lib/util.statistic.ts | 49 ++++++++++++++ packages/@aws-cdk/cloudwatch/package.json | 3 +- .../@aws-cdk/cloudwatch/test/test.metrics.ts | 33 ++++++++++ 7 files changed, 108 insertions(+), 53 deletions(-) create mode 100644 packages/@aws-cdk/cloudwatch/lib/util.statistic.ts create mode 100644 packages/@aws-cdk/cloudwatch/test/test.metrics.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 07bba78123347..d797167c29b26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ ## [UNRELEASED] + * CloudWatch: add `Metric.grantPutMetricData()` to give `PutMetricData` permissions to IAM + identities. ([#258]) * [FIXED] `cdk docs` works but a message __Unknown command: docs__ is printed ([#256]) +[#258]: https://github.com/awslabs/aws-cdk/pull/258 + ## 0.7.3 - 2018-07-09 ### Highlights diff --git a/packages/@aws-cdk/cloudwatch/lib/alarm.ts b/packages/@aws-cdk/cloudwatch/lib/alarm.ts index 14af5b9a91dd0..01b104b1b5d69 100644 --- a/packages/@aws-cdk/cloudwatch/lib/alarm.ts +++ b/packages/@aws-cdk/cloudwatch/lib/alarm.ts @@ -1,7 +1,8 @@ import { Arn, Construct, Token } from '@aws-cdk/core'; import { cloudwatch } from '@aws-cdk/resources'; import { HorizontalAnnotation } from './graph'; -import { Dimension, Metric, parseStatistic, Statistic, Unit } from './metric'; +import { Dimension, Metric, Statistic, Unit } from './metric'; +import { parseStatistic } from './util.statistic'; /** * Properties for Alarms diff --git a/packages/@aws-cdk/cloudwatch/lib/graph.ts b/packages/@aws-cdk/cloudwatch/lib/graph.ts index c71c18e589108..4fe343a9bdd19 100644 --- a/packages/@aws-cdk/cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/cloudwatch/lib/graph.ts @@ -1,6 +1,7 @@ import { AwsRegion, Token } from "@aws-cdk/core"; import { Alarm } from "./alarm"; -import { Metric, parseStatistic } from "./metric"; +import { Metric } from "./metric"; +import { parseStatistic } from './util.statistic'; import { ConcreteWidget } from "./widget"; /** diff --git a/packages/@aws-cdk/cloudwatch/lib/metric.ts b/packages/@aws-cdk/cloudwatch/lib/metric.ts index ceb2cfbd73d8c..2d986b550614b 100644 --- a/packages/@aws-cdk/cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/cloudwatch/lib/metric.ts @@ -1,5 +1,7 @@ -import { Construct } from "@aws-cdk/core"; +import { Construct, PolicyStatement } from "@aws-cdk/core"; +import { IIdentityResource } from "@aws-cdk/iam"; import { Alarm, ComparisonOperator, TreatMissingData } from "./alarm"; +import { parseStatistic } from './util.statistic'; export type DimensionHash = {[dim: string]: any}; @@ -80,6 +82,19 @@ export interface MetricProps { * alarms and graphs. */ export class Metric { + /** + * Grant permissions to the given identity to write metrics. + * + * @param identity The IAM identity to give permissions to. + */ + public static grantPutMetricData(identity?: IIdentityResource) { + if (!identity) { return; } + + identity.addToPolicy(new PolicyStatement() + .addAllResources() + .addAction("cloudwatch:PutMetricData")); + } + public readonly dimensions?: DimensionHash; public readonly namespace: string; public readonly metricName: string; @@ -366,53 +381,4 @@ function ifUndefined(x: T | undefined, def: T | undefined): T | undefined { return x; } return def; -} - -interface SimpleStatistic { - type: 'simple'; - statistic: Statistic; -} - -interface PercentileStatistic { - type: 'percentile'; - percentile: number; -} - -/** - * Parse a statistic, returning the type of metric that was used (simple or percentile) - */ -export function parseStatistic(stat: string): SimpleStatistic | PercentileStatistic { - const lowerStat = stat.toLowerCase(); - - // Simple statistics - const statMap: {[k: string]: Statistic} = { - average: Statistic.Average, - avg: Statistic.Average, - minimum: Statistic.Minimum, - min: Statistic.Minimum, - maximum: Statistic.Maximum, - max: Statistic.Maximum, - samplecount: Statistic.SampleCount, - n: Statistic.SampleCount, - sum: Statistic.Sum, - }; - - if (lowerStat in statMap) { - return { - type: 'simple', - statistic: statMap[lowerStat] - }; - } - - // Percentile statistics - const re = /^p([\d.]+)$/; - const m = re.exec(lowerStat); - if (m) { - return { - type: 'percentile', - percentile: parseFloat(m[1]) - }; - } - - throw new Error(`Not a valid statistic: '${stat}', must be one of Average | Minimum | Maximum | SampleCount | Sum | pNN.NN`); } \ No newline at end of file diff --git a/packages/@aws-cdk/cloudwatch/lib/util.statistic.ts b/packages/@aws-cdk/cloudwatch/lib/util.statistic.ts new file mode 100644 index 0000000000000..ec7aa0c586202 --- /dev/null +++ b/packages/@aws-cdk/cloudwatch/lib/util.statistic.ts @@ -0,0 +1,49 @@ +import { Statistic } from "./metric"; + +export interface SimpleStatistic { + type: 'simple'; + statistic: Statistic; +} + +export interface PercentileStatistic { + type: 'percentile'; + percentile: number; +} +/** + * Parse a statistic, returning the type of metric that was used (simple or percentile) + */ +export function parseStatistic(stat: string): SimpleStatistic | PercentileStatistic { + const lowerStat = stat.toLowerCase(); + + // Simple statistics + const statMap: {[k: string]: Statistic} = { + average: Statistic.Average, + avg: Statistic.Average, + minimum: Statistic.Minimum, + min: Statistic.Minimum, + maximum: Statistic.Maximum, + max: Statistic.Maximum, + samplecount: Statistic.SampleCount, + n: Statistic.SampleCount, + sum: Statistic.Sum, + }; + + if (lowerStat in statMap) { + return { + type: 'simple', + statistic: statMap[lowerStat] + }; + } + + // Percentile statistics + const re = /^p([\d.]+)$/; + const m = re.exec(lowerStat); + if (m) { + return { + type: 'percentile', + percentile: parseFloat(m[1]) + }; + } + + throw new Error(`Not a valid statistic: '${stat}', must be one of Average | Minimum | Maximum | SampleCount | Sum | pNN.NN`); +} \ No newline at end of file diff --git a/packages/@aws-cdk/cloudwatch/package.json b/packages/@aws-cdk/cloudwatch/package.json index dccb776bbd2ce..389acc7b1c8ba 100644 --- a/packages/@aws-cdk/cloudwatch/package.json +++ b/packages/@aws-cdk/cloudwatch/package.json @@ -42,6 +42,7 @@ }, "dependencies": { "@aws-cdk/core": "^0.7.3-beta", - "@aws-cdk/resources": "^0.7.3-beta" + "@aws-cdk/resources": "^0.7.3-beta", + "@aws-cdk/iam": "^0.7.3-beta" } } diff --git a/packages/@aws-cdk/cloudwatch/test/test.metrics.ts b/packages/@aws-cdk/cloudwatch/test/test.metrics.ts new file mode 100644 index 0000000000000..eb88439b7cb8e --- /dev/null +++ b/packages/@aws-cdk/cloudwatch/test/test.metrics.ts @@ -0,0 +1,33 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import { Anyone, Stack } from '@aws-cdk/core'; +import { Role } from '@aws-cdk/iam'; +import { Test } from 'nodeunit'; +import { Metric } from '../lib'; + +export = { + 'metric grant'(test: Test) { + // GIVEN + const stack = new Stack(); + const role = new Role(stack, 'SomeRole', { + assumedBy: new Anyone() + }); + + // WHEN + Metric.grantPutMetricData(role); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: "cloudwatch:PutMetricData", + Effect: "Allow", + Resource: "*" + } + ], + }, + })); + + test.done(); + } +}; \ No newline at end of file