Skip to content

Commit

Permalink
Merge pull request #22556 from hashicorp/f-iso-tagging-cloudwatch
Browse files Browse the repository at this point in the history
cloudwatch: ISO-friendly tagging
  • Loading branch information
YakDriver authored Jan 13, 2022
2 parents 37210e7 + 4f4350b commit c6f9483
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 11 deletions.
11 changes: 11 additions & 0 deletions .changelog/22556.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```release-note:enhancement
resource/aws_cloudwatch_composite_alarm: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO)
```

```release-note:enhancement
resource/aws_cloudwatch_metric_alarm: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO)
```

```release-note:enhancement
resource/aws_cloudwatch_metric_stream: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO)
```
52 changes: 50 additions & 2 deletions internal/service/cloudwatch/composite_alarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,45 @@ func resourceCompositeAlarmCreate(ctx context.Context, d *schema.ResourceData, m
input := expandPutCompositeAlarmInput(d, meta)

_, err := conn.PutCompositeAlarmWithContext(ctx, &input)

// Some partitions (i.e., ISO) may not support tag-on-create
if input.Tags != nil && (tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault)) {
log.Printf("[WARN] CloudWatch Composite Alarm (%s) create failed (%s) with tags. Trying create without tags.", d.Id(), err)
input.Tags = nil

_, err = conn.PutCompositeAlarmWithContext(ctx, &input)
}

if err != nil {
return diag.Errorf("error creating CloudWatch Composite Alarm (%s): %s", name, err)
}

d.SetId(name)

defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{})))

// Some partitions (i.e., ISO) may not support tag-on-create, attempt tag after create
if input.Tags == nil && len(tags) > 0 {
alarm, err := FindCompositeAlarmByName(ctx, conn, name)

if err != nil {
return diag.Errorf("error reading CloudWatch Composite Alarm (%s): %s", name, err)
}

err = UpdateTags(conn, aws.StringValue(alarm.AlarmArn), nil, tags)

// If default tags only, log and continue. Otherwise, error.
if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault)) {
log.Printf("[WARN] error adding tags after create for CloudWatch Composite Alarm (%s): %s", d.Id(), err)
return resourceCompositeAlarmRead(ctx, d, meta)
}

if err != nil {
return diag.Errorf("error creating CloudWatch Composite Alarm (%s) tags: %s", d.Id(), err)
}
}

return resourceCompositeAlarmRead(ctx, d, meta)
}

Expand Down Expand Up @@ -156,6 +189,13 @@ func resourceCompositeAlarmRead(ctx context.Context, d *schema.ResourceData, met
}

tags, err := ListTags(conn, aws.StringValue(alarm.AlarmArn))

// Some partitions (i.e., ISO) may not support tagging, giving error
if tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault) {
log.Printf("[WARN] Unable to list tags for CloudWatch Composite Alarm %s: %s", d.Id(), err)
return nil
}

if err != nil {
return diag.Errorf("error listing tags of alarm: %s", err)
}
Expand Down Expand Up @@ -189,8 +229,16 @@ func resourceCompositeAlarmUpdate(ctx context.Context, d *schema.ResourceData, m
if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

if err := UpdateTags(conn, arn, o, n); err != nil {
return diag.Errorf("error updating tags: %s", err)
err := UpdateTags(conn, arn, o, n)

// Some partitions (i.e., ISO) may not support tagging, giving error
if tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault) {
log.Printf("[WARN] Unable to update tags for CloudWatch Composite Alarm %s: %s", arn, err)
return resourceCompositeAlarmRead(ctx, d, meta)
}

if err != nil {
return diag.Errorf("error updating CloudWatch Composite Alarm (%s) tags: %s", arn, err)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package cloudwatch

const (
errCodeAccessDenied = "AccessDenied"
)

const (
lowSampleCountPercentilesEvaluate = "evaluate"
lowSampleCountPercentilesmissingDataIgnore = "ignore"
Expand Down
55 changes: 53 additions & 2 deletions internal/service/cloudwatch/metric_alarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,46 @@ func resourceMetricAlarmCreate(d *schema.ResourceData, meta interface{}) error {

log.Printf("[DEBUG] Creating CloudWatch Metric Alarm: %#v", params)
_, err = conn.PutMetricAlarm(&params)

// Some partitions (i.e., ISO) may not support tag-on-create
if params.Tags != nil && (tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault)) {
log.Printf("[WARN] CloudWatch Metric Alarm (%s) create failed (%s) with tags. Trying create without tags.", d.Id(), err)
params.Tags = nil

_, err = conn.PutMetricAlarm(&params)
}

if err != nil {
return fmt.Errorf("Creating metric alarm failed: %w", err)
}

d.SetId(d.Get("alarm_name").(string))
log.Println("[INFO] CloudWatch Metric Alarm created")

defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{})))

// Some partitions (i.e., ISO) may not support tag-on-create, attempt tag after create
if params.Tags == nil && len(tags) > 0 {
resp, err := FindMetricAlarmByName(conn, d.Id())

if err != nil {
return fmt.Errorf("while finding metric alarm (%s): %w", d.Id(), err)
}

err = UpdateTags(conn, aws.StringValue(resp.AlarmArn), nil, tags)

// If default tags only, log and continue. Otherwise, error.
if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault)) {
log.Printf("[WARN] could not add tags after create for CloudWatch Metric Alarm (%s): %s", d.Id(), err)
return resourceMetricAlarmRead(d, meta)
}

if err != nil {
return fmt.Errorf("creating CloudWatch Metric Alarm (%s) tags: %w", d.Id(), err)
}
}

return resourceMetricAlarmRead(d, meta)
}

Expand Down Expand Up @@ -372,6 +406,12 @@ func resourceMetricAlarmRead(d *schema.ResourceData, meta interface{}) error {

tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

// Some partitions (i.e., ISO) may not support tagging, giving error
if tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault) {
log.Printf("[WARN] Unable to list tags for CloudWatch Metric Alarm %s: %s", d.Id(), err)
return nil
}

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
Expand Down Expand Up @@ -399,7 +439,15 @@ func resourceMetricAlarmUpdate(d *schema.ResourceData, meta interface{}) error {
if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

if err := UpdateTags(conn, arn, o, n); err != nil {
err := UpdateTags(conn, arn, o, n)

// Some partitions (i.e., ISO) may not support tagging, giving error
if tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault) {
log.Printf("[WARN] Unable to update tags for CloudWatch Metric Alarm %s: %s", arn, err)
return resourceMetricAlarmRead(d, meta)
}

if err != nil {
return fmt.Errorf("error updating CloudWatch Metric Alarm (%s) tags: %w", arn, err)
}
}
Expand Down Expand Up @@ -435,7 +483,10 @@ func getPutMetricAlarmInput(d *schema.ResourceData, meta interface{}) cloudwatch
ComparisonOperator: aws.String(d.Get("comparison_operator").(string)),
EvaluationPeriods: aws.Int64(int64(d.Get("evaluation_periods").(int))),
TreatMissingData: aws.String(d.Get("treat_missing_data").(string)),
Tags: Tags(tags.IgnoreAWS()),
}

if len(tags) > 0 {
params.Tags = Tags(tags.IgnoreAWS())
}

if v := d.Get("actions_enabled"); v != nil {
Expand Down
48 changes: 41 additions & 7 deletions internal/service/cloudwatch/metric_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ func resourceMetricStreamCreate(ctx context.Context, d *schema.ResourceData, met
FirehoseArn: aws.String(d.Get("firehose_arn").(string)),
RoleArn: aws.String(d.Get("role_arn").(string)),
OutputFormat: aws.String(d.Get("output_format").(string)),
Tags: Tags(tags.IgnoreAWS()),
}

if len(tags) > 0 {
params.Tags = Tags(tags.IgnoreAWS())
}

if v, ok := d.GetOk("include_filter"); ok && v.(*schema.Set).Len() > 0 {
Expand All @@ -142,13 +145,38 @@ func resourceMetricStreamCreate(ctx context.Context, d *schema.ResourceData, met
params.ExcludeFilters = expandCloudWatchMetricStreamFilters(v.(*schema.Set))
}

log.Printf("[DEBUG] Putting CloudWatch MetricStream: %#v", params)
_, err := conn.PutMetricStreamWithContext(ctx, &params)
log.Printf("[DEBUG] Putting CloudWatch Metric Stream: %#v", params)
output, err := conn.PutMetricStreamWithContext(ctx, &params)

// Some partitions (i.e., ISO) may not support tag-on-create
if params.Tags != nil && (tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault)) {
log.Printf("[WARN] CloudWatch Metric Stream (%s) create failed (%s) with tags. Trying create without tags.", d.Id(), err)
params.Tags = nil

output, err = conn.PutMetricStreamWithContext(ctx, &params)
}

if err != nil {
return diag.FromErr(fmt.Errorf("putting metric_stream failed: %s", err))
}

d.SetId(name)
log.Println("[INFO] CloudWatch MetricStream put finished")
log.Println("[INFO] CloudWatch Metric Stream put finished")

// Some partitions (i.e., ISO) may not support tag-on-create, attempt tag after create
if params.Tags == nil && len(tags) > 0 {
err := UpdateTags(conn, aws.StringValue(output.Arn), nil, tags)

// If default tags only, log and continue. Otherwise, error.
if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault)) {
log.Printf("[WARN] error adding tags after create for CloudWatch Metric Stream (%s): %s", d.Id(), err)
return resourceMetricStreamRead(ctx, d, meta)
}

if err != nil {
return diag.Errorf("error creating CloudWatch Metric Stream (%s) tags: %s", d.Id(), err)
}
}

return resourceMetricStreamRead(ctx, d, meta)
}
Expand Down Expand Up @@ -198,6 +226,12 @@ func resourceMetricStreamRead(ctx context.Context, d *schema.ResourceData, meta

tags, err := ListTags(conn, aws.StringValue(output.Arn))

// Some partitions (i.e., ISO) may not support tagging, giving error
if tfawserr.ErrCodeContains(err, errCodeAccessDenied) || tfawserr.ErrCodeContains(err, cloudwatch.ErrCodeInternalServiceFault) {
log.Printf("[WARN] Unable to list tags for CloudWatch Metric Stream %s: %s", d.Id(), err)
return nil
}

if err != nil {
return diag.FromErr(fmt.Errorf("error listing tags for CloudWatch Metric Stream (%s): %w", d.Id(), err))
}
Expand All @@ -217,21 +251,21 @@ func resourceMetricStreamRead(ctx context.Context, d *schema.ResourceData, meta
}

func resourceMetricStreamDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
log.Printf("[INFO] Deleting CloudWatch MetricStream %s", d.Id())
log.Printf("[INFO] Deleting CloudWatch Metric Stream %s", d.Id())
conn := meta.(*conns.AWSClient).CloudWatchConn
params := cloudwatch.DeleteMetricStreamInput{
Name: aws.String(d.Id()),
}

if _, err := conn.DeleteMetricStreamWithContext(ctx, &params); err != nil {
return diag.FromErr(fmt.Errorf("error deleting CloudWatch MetricStream: %s", err))
return diag.FromErr(fmt.Errorf("error deleting CloudWatch Metric Stream: %s", err))
}

if _, err := WaitMetricStreamDeleted(ctx, conn, d.Id()); err != nil {
return diag.FromErr(fmt.Errorf("error while waiting for CloudWatch Metric Stream (%s) to become deleted: %w", d.Id(), err))
}

log.Printf("[INFO] CloudWatch MetricStream %s deleted", d.Id())
log.Printf("[INFO] CloudWatch Metric Stream %s deleted", d.Id())

return nil
}
Expand Down

0 comments on commit c6f9483

Please sign in to comment.