Skip to content

Commit ee3aa66

Browse files
committed
Support modern extended statistics for CloudWatch metric alarm
1 parent bb560f7 commit ee3aa66

File tree

2 files changed

+144
-6
lines changed

2 files changed

+144
-6
lines changed

internal/service/cloudwatch/metric_alarm.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ func ResourceMetricAlarm() *schema.Resource {
111111
Required: true,
112112
ValidateFunc: validation.Any(
113113
validation.StringInSlice(cloudwatch.Statistic_Values(), false),
114-
validation.StringMatch(regexp.MustCompile(`p(\d{1,2}(\.\d{0,2})?|100)`), "must specify a value between p0.0 and p100"),
114+
validation.StringMatch(
115+
// doesn't catch: PR with %-values provided, TM/WM/PR/TC/TS with no values provided
116+
regexp.MustCompile(`^((p|(tm)|(wm)|(tc)|(ts))((\d{1,2}(\.\d{1,2})?)|(100))|(IQM)|(((TM)|(WM)|(PR)|(TC)|(TS)))\((\d+(\.\d+)?%?)?:(\d+(\.\d+)?%?)?\))$`),
117+
"invalid statistic, see: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Statistics-definitions.html",
118+
),
115119
),
116120
},
117121
"unit": {
@@ -235,7 +239,11 @@ func ResourceMetricAlarm() *schema.Resource {
235239
Type: schema.TypeString,
236240
Optional: true,
237241
ConflictsWith: []string{"statistic", "metric_query"},
238-
ValidateFunc: validation.StringMatch(regexp.MustCompile(`p(\d{1,2}(\.\d{0,2})?|100)`), "must specify a value between p0.0 and p100"),
242+
ValidateFunc: validation.StringMatch(
243+
// doesn't catch: PR with %-values provided, TM/WM/PR/TC/TS with no values provided
244+
regexp.MustCompile(`^((p|(tm)|(wm)|(tc)|(ts))((\d{1,2}(\.\d{1,2})?)|(100))|(IQM)|(((TM)|(WM)|(PR)|(TC)|(TS)))\((\d+(\.\d+)?%?)?:(\d+(\.\d+)?%?)?\))$`),
245+
"invalid statistic, see: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Statistics-definitions.html",
246+
),
239247
},
240248
"treat_missing_data": {
241249
Type: schema.TypeString,

internal/service/cloudwatch/metric_alarm_test.go

+134-4
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,142 @@ func TestAccCloudWatchMetricAlarm_extendedStatistic(t *testing.T) {
262262
CheckDestroy: testAccCheckMetricAlarmDestroy,
263263
Steps: []resource.TestStep{
264264
{
265-
Config: testAccMetricAlarmExtendedStatisticConfig(rName),
265+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "p88.0"),
266266
Check: resource.ComposeTestCheckFunc(
267267
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
268268
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "p88.0"),
269269
),
270270
},
271+
{
272+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "p0.0"),
273+
Check: resource.ComposeTestCheckFunc(
274+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
275+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "p0.0"),
276+
),
277+
},
278+
{
279+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "p100"),
280+
Check: resource.ComposeTestCheckFunc(
281+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
282+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "p100"),
283+
),
284+
},
285+
{
286+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "p95"),
287+
Check: resource.ComposeTestCheckFunc(
288+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
289+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "p95"),
290+
),
291+
},
292+
{
293+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "tm90"),
294+
Check: resource.ComposeTestCheckFunc(
295+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
296+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "tm90"),
297+
),
298+
},
299+
{
300+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "TM(2%:98%)"),
301+
Check: resource.ComposeTestCheckFunc(
302+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
303+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "TM(2%:98%)"),
304+
),
305+
},
306+
{
307+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "TM(150:1000)"),
308+
Check: resource.ComposeTestCheckFunc(
309+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
310+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "TM(150:1000)"),
311+
),
312+
},
313+
{
314+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "IQM"),
315+
Check: resource.ComposeTestCheckFunc(
316+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
317+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "IQM"),
318+
),
319+
},
320+
{
321+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "wm98"),
322+
Check: resource.ComposeTestCheckFunc(
323+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
324+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "wm98"),
325+
),
326+
},
327+
{
328+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "PR(:300)"),
329+
Check: resource.ComposeTestCheckFunc(
330+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
331+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "PR(:300)"),
332+
),
333+
},
334+
{
335+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "PR(100:2000)"),
336+
Check: resource.ComposeTestCheckFunc(
337+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
338+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "PR(100:2000)"),
339+
),
340+
},
341+
{
342+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "tc90"),
343+
Check: resource.ComposeTestCheckFunc(
344+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
345+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "tc90"),
346+
),
347+
},
348+
{
349+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "TC(0.005:0.030)"),
350+
Check: resource.ComposeTestCheckFunc(
351+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
352+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "TC(0.005:0.030)"),
353+
),
354+
},
355+
{
356+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "TS(80%:)"),
357+
Check: resource.ComposeTestCheckFunc(
358+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
359+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "TS(80%:)"),
360+
),
361+
},
362+
{
363+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "TC(:0.5)"),
364+
Check: resource.ComposeTestCheckFunc(
365+
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
366+
resource.TestCheckResourceAttr(resourceName, "extended_statistic", "TC(:0.5)"),
367+
),
368+
},
369+
{
370+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "IQM(1:2)"), // IQM accepts no args
371+
ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
372+
},
373+
{
374+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "iqm10"), // IQM accepts no args
375+
ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
376+
},
377+
// { TODO: more complex regex to reject this
378+
// Config: testAccMetricAlarmExtendedStatisticConfig(rName, "PR(5%:10%)"), // PR args must be absolute
379+
// ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
380+
// },
381+
// { TODO: more complex regex to reject this
382+
// Config: testAccMetricAlarmExtendedStatisticConfig(rName, "TC(:)"), // at least one arg must be provided
383+
// ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
384+
// },
385+
{
386+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "WM"), // missing syntax
387+
ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
388+
},
389+
{
390+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "p"), // missing arg
391+
ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
392+
},
393+
{
394+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "AB(1:2)"), // unknown stat 'AB'
395+
ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
396+
},
397+
{
398+
Config: testAccMetricAlarmExtendedStatisticConfig(rName, "cd42"), // unknown stat 'cd'
399+
ExpectError: regexp.MustCompile(`invalid statistic, see: https:\/\/docs\.aws\.amazon\.com\/.*`),
400+
},
271401
},
272402
})
273403
}
@@ -614,7 +744,7 @@ resource "aws_cloudwatch_metric_alarm" "test" {
614744
`, rName)
615745
}
616746

617-
func testAccMetricAlarmExtendedStatisticConfig(rName string) string {
747+
func testAccMetricAlarmExtendedStatisticConfig(rName string, stat string) string {
618748
return fmt.Sprintf(`
619749
resource "aws_cloudwatch_metric_alarm" "test" {
620750
alarm_name = "%s"
@@ -623,7 +753,7 @@ resource "aws_cloudwatch_metric_alarm" "test" {
623753
metric_name = "CPUUtilization"
624754
namespace = "AWS/EC2"
625755
period = "120"
626-
extended_statistic = "p88.0"
756+
extended_statistic = "%s"
627757
threshold = "80"
628758
alarm_description = "This metric monitors ec2 cpu utilization"
629759
insufficient_data_actions = []
@@ -632,7 +762,7 @@ resource "aws_cloudwatch_metric_alarm" "test" {
632762
InstanceId = "i-abc123"
633763
}
634764
}
635-
`, rName)
765+
`, rName, stat)
636766
}
637767

638768
func testAccMetricAlarmMissingStatisticConfig(rName string) string {

0 commit comments

Comments
 (0)