Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/cloudwatch_metric_alarm - add validations #12817

Merged
merged 10 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/12817.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_cloudwatch_metric_alarm: Add plan time validation to `alarm_name`, `comparison_operator`, `metric_name`, `metric_query.id`, `metric_query.expression`, `metric_query.metric.metric_name`, `metric_query.metric.namespace`, `metric_query.metric.unit`, `namespace`, `period`, `statistic`, `alarm_description`, `insufficient_data_actions`, `ok_actions`, `unit`, and `extended_statistic`
```
129 changes: 65 additions & 64 deletions aws/resource_aws_cloudwatch_metric_alarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package aws
import (
"fmt"
"log"
"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
Expand All @@ -27,17 +28,19 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {

Schema: map[string]*schema.Schema{
"alarm_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(1, 255),
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"comparison_operator": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(cloudwatch.ComparisonOperator_Values(), false),
},
"evaluation_periods": {
Type: schema.TypeInt,
Expand All @@ -48,6 +51,7 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"metric_query"},
ValidateFunc: validation.StringLenBetween(1, 255),
},
"metric_query": {
Type: schema.TypeSet,
Expand All @@ -56,12 +60,14 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(1, 255),
},
"expression": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(1, 1024),
},
"metric": {
Type: schema.TypeList,
Expand All @@ -75,12 +81,17 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
},
"metric_name": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(1, 255),
},
"namespace": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.Any(
bflad marked this conversation as resolved.
Show resolved Hide resolved
validation.StringLenBetween(1, 255),
validation.StringMatch(regexp.MustCompile(`[^:].*`), ""),
bflad marked this conversation as resolved.
Show resolved Hide resolved
),
},
"period": {
Type: schema.TypeInt,
Expand All @@ -91,8 +102,9 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Required: true,
},
"unit": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(cloudwatch.StandardUnit_Values(), false),
},
},
},
Expand All @@ -113,16 +125,25 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"metric_query"},
ValidateFunc: validation.Any(
bflad marked this conversation as resolved.
Show resolved Hide resolved
validation.StringLenBetween(1, 255),
validation.StringMatch(regexp.MustCompile(`[^:].*`), ""),
bflad marked this conversation as resolved.
Show resolved Hide resolved
),
},
"period": {
Type: schema.TypeInt,
Optional: true,
ConflictsWith: []string{"metric_query"},
ValidateFunc: validation.Any(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is valid 👍

validation.IntInSlice([]int{10, 30}),
validation.IntDivisibleBy(60),
),
},
"statistic": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"extended_statistic", "metric_query"},
ValidateFunc: validation.StringInSlice(cloudwatch.Statistic_Values(), false),
},
"threshold": {
Type: schema.TypeFloat,
Expand Down Expand Up @@ -153,8 +174,9 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Set: schema.HashString,
},
"alarm_description": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(0, 1024),
},
"datapoints_to_alarm": {
Type: schema.TypeInt,
Expand All @@ -170,23 +192,31 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
"insufficient_data_actions": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
MaxItems: 5,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateArn,
bflad marked this conversation as resolved.
Show resolved Hide resolved
},
},
"ok_actions": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
MaxItems: 5,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateArn,
bflad marked this conversation as resolved.
Show resolved Hide resolved
},
},
"unit": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(cloudwatch.StandardUnit_Values(), false),
},
"extended_statistic": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"statistic", "metric_query"},
ValidateFunc: validation.StringMatch(regexp.MustCompile(`p(\d{1,2}(\.\d{0,2})?|100)`), ""),
bflad marked this conversation as resolved.
Show resolved Hide resolved
},
"treat_missing_data": {
Type: schema.TypeString,
Expand Down Expand Up @@ -268,7 +298,7 @@ func resourceAwsCloudWatchMetricAlarmRead(d *schema.ResourceData, meta interface

d.Set("actions_enabled", resp.ActionsEnabled)

if err := d.Set("alarm_actions", _strArrPtrToList(resp.AlarmActions)); err != nil {
if err := d.Set("alarm_actions", flattenStringSet(resp.AlarmActions)); err != nil {
log.Printf("[WARN] Error setting Alarm Actions: %s", err)
}
arn := *resp.AlarmArn
Expand All @@ -282,7 +312,7 @@ func resourceAwsCloudWatchMetricAlarmRead(d *schema.ResourceData, meta interface
}
d.Set("evaluation_periods", resp.EvaluationPeriods)

if err := d.Set("insufficient_data_actions", _strArrPtrToList(resp.InsufficientDataActions)); err != nil {
if err := d.Set("insufficient_data_actions", flattenStringSet(resp.InsufficientDataActions)); err != nil {
log.Printf("[WARN] Error setting Insufficient Data Actions: %s", err)
}
d.Set("metric_name", resp.MetricName)
Expand Down Expand Up @@ -315,7 +345,7 @@ func resourceAwsCloudWatchMetricAlarmRead(d *schema.ResourceData, meta interface
}
}

if err := d.Set("ok_actions", _strArrPtrToList(resp.OKActions)); err != nil {
if err := d.Set("ok_actions", flattenStringSet(resp.OKActions)); err != nil {
log.Printf("[WARN] Error setting OK Actions: %s", err)
}
d.Set("period", resp.Period)
Expand Down Expand Up @@ -364,23 +394,17 @@ func resourceAwsCloudWatchMetricAlarmUpdate(d *schema.ResourceData, meta interfa
}

func resourceAwsCloudWatchMetricAlarmDelete(d *schema.ResourceData, meta interface{}) error {
resp, err := getAwsCloudWatchMetricAlarm(d, meta)
if err != nil {
return err
}
if resp == nil {
log.Printf("[DEBUG] CloudWatch Metric Alarm %s is already gone", d.Id())
return nil
}

log.Printf("[INFO] Deleting CloudWatch Metric Alarm: %s", d.Id())

conn := meta.(*AWSClient).cloudwatchconn
params := cloudwatch.DeleteAlarmsInput{
AlarmNames: []*string{aws.String(d.Id())},
}

log.Printf("[INFO] Deleting CloudWatch Metric Alarm: %s", d.Id())

if _, err := conn.DeleteAlarms(&params); err != nil {
if isAWSErr(err, cloudwatch.ErrCodeResourceNotFoundException, "") {
return nil
}
return fmt.Errorf("Error deleting CloudWatch Metric Alarm: %s", err)
}
log.Println("[INFO] CloudWatch Metric Alarm deleted")
Expand Down Expand Up @@ -442,22 +466,12 @@ func getAwsCloudWatchPutMetricAlarmInput(d *schema.ResourceData) cloudwatch.PutM
params.Threshold = aws.Float64(d.Get("threshold").(float64))
}

var alarmActions []*string
if v := d.Get("alarm_actions"); v != nil {
for _, v := range v.(*schema.Set).List() {
str := v.(string)
alarmActions = append(alarmActions, aws.String(str))
}
params.AlarmActions = alarmActions
if v, ok := d.GetOk("alarm_actions"); ok {
params.AlarmActions = expandStringSet(v.(*schema.Set))
}

var insufficientDataActions []*string
if v := d.Get("insufficient_data_actions"); v != nil {
for _, v := range v.(*schema.Set).List() {
str := v.(string)
insufficientDataActions = append(insufficientDataActions, aws.String(str))
}
params.InsufficientDataActions = insufficientDataActions
if v, ok := d.GetOk("insufficient_data_actions"); ok {
params.InsufficientDataActions = expandStringSet(v.(*schema.Set))
}

var metrics []*cloudwatch.MetricDataQuery
Expand Down Expand Up @@ -516,13 +530,8 @@ func getAwsCloudWatchPutMetricAlarmInput(d *schema.ResourceData) cloudwatch.PutM
params.Metrics = metrics
}

var okActions []*string
if v := d.Get("ok_actions"); v != nil {
for _, v := range v.(*schema.Set).List() {
str := v.(string)
okActions = append(okActions, aws.String(str))
}
params.OKActions = okActions
if v, ok := d.GetOk("ok_actions"); ok {
params.OKActions = expandStringSet(v.(*schema.Set))
}

a := d.Get("dimensions").(map[string]interface{})
Expand Down Expand Up @@ -560,14 +569,6 @@ func getAwsCloudWatchMetricAlarm(d *schema.ResourceData, meta interface{}) (*clo
return nil, nil
}

func _strArrPtrToList(strArrPtr []*string) []string {
var result []string
for _, elem := range strArrPtr {
result = append(result, aws.StringValue(elem))
}
return result
}

func flattenDimensions(dims []*cloudwatch.Dimension) map[string]interface{} {
flatDims := make(map[string]interface{})
for _, d := range dims {
Expand Down
22 changes: 22 additions & 0 deletions aws/resource_aws_cloudwatch_metric_alarm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,28 @@ func TestAccAWSCloudWatchMetricAlarm_tags(t *testing.T) {
})
}

func TestAccAWSCloudWatchMetricAlarm_disappears(t *testing.T) {
var alarm cloudwatch.MetricAlarm
resourceName := "aws_cloudwatch_metric_alarm.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSCloudWatchMetricAlarmConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists(resourceName, &alarm),
testAccCheckResourceDisappears(testAccProvider, resourceAwsCloudWatchMetricAlarm(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckCloudWatchMetricAlarmDimension(n, k, v string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down