Skip to content

Commit

Permalink
Merge pull request #22551 from hashicorp/f-iso-tagging-elbv2
Browse files Browse the repository at this point in the history
elbv2: ISO-friendly tagging
  • Loading branch information
YakDriver authored Jan 13, 2022
2 parents a5c25e9 + 021531b commit 27d127b
Show file tree
Hide file tree
Showing 12 changed files with 444 additions and 163 deletions.
27 changes: 27 additions & 0 deletions .changelog/22551.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
```release-note:enhancement
resource/aws_lb_listener: 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
data-source/aws_lb_listener: Allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO)
```

```release-note:enhancement
resource/aws_lb_listener_rule: 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_lb: 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
data-source/aws_lb: Allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO)
```

```release-note:enhancement
resource/aws_lb_target_group: 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
data-source/aws_lb_target_group: Allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO)
```
5 changes: 5 additions & 0 deletions internal/service/elbv2/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package elbv2

const (
ErrCodeAccessDenied = "AccessDenied"
)
89 changes: 66 additions & 23 deletions internal/service/elbv2/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,38 +432,36 @@ func resourceListenerCreate(d *schema.ResourceData, meta interface{}) error {
}
}

var output *elbv2.CreateListenerOutput

err := resource.Retry(loadBalancerListenerCreateTimeout, func() *resource.RetryError {
var err error

output, err = conn.CreateListener(params)

if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeCertificateNotFoundException) {
return resource.RetryableError(err)
}

if err != nil {
return resource.NonRetryableError(err)
}

return nil
})
output, err := retryListenerCreate(conn, params)

if tfresource.TimedOut(err) {
output, err = conn.CreateListener(params)
// Some partitions may not support tag-on-create
if params.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) {
log.Printf("[WARN] ELBv2 Listener (%s) create failed (%s) with tags. Trying create without tags.", lbArn, err)
params.Tags = nil
output, err = retryListenerCreate(conn, params)
}

if err != nil {
return fmt.Errorf("error creating ELBv2 Listener (%s): %w", lbArn, err)
}

if output == nil || len(output.Listeners) == 0 {
return fmt.Errorf("error creating ELBv2 Listener: no listeners returned in response")
}

d.SetId(aws.StringValue(output.Listeners[0].ListenerArn))

// Post-create tagging supported in some partitions
if params.Tags == nil && len(tags) > 0 {
err := UpdateTags(conn, d.Id(), nil, tags)

if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) {
// if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be)
log.Printf("[WARN] error adding tags after create for ELBv2 Listener (%s): %s", d.Id(), err)
return resourceListenerRead(d, meta)
}

if err != nil {
return fmt.Errorf("error creating ELBv2 Listener (%s) tags: %w", d.Id(), err)
}
}

return resourceListenerRead(d, meta)
}

Expand Down Expand Up @@ -536,6 +534,11 @@ func resourceListenerRead(d *schema.ResourceData, meta interface{}) error {

tags, err := ListTags(conn, d.Id())

if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) {
log.Printf("[WARN] Unable to list tags for ELBv2 Listener %s: %s", d.Id(), err)
return nil
}

if err != nil {
return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err)
}
Expand Down Expand Up @@ -639,6 +642,12 @@ func resourceListenerUpdate(d *schema.ResourceData, meta interface{}) error {
err = UpdateTags(conn, d.Id(), o, n)
}

// ISO partitions may not support tagging, giving error
if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) {
log.Printf("[WARN] Unable to update tags for ELBv2 Listener %s: %s", d.Id(), err)
return resourceListenerRead(d, meta)
}

if err != nil {
return fmt.Errorf("error updating LB (%s) tags: %w", d.Id(), err)
}
Expand All @@ -660,6 +669,40 @@ func resourceListenerDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}

func retryListenerCreate(conn *elbv2.ELBV2, params *elbv2.CreateListenerInput) (*elbv2.CreateListenerOutput, error) {
var output *elbv2.CreateListenerOutput

err := resource.Retry(loadBalancerListenerCreateTimeout, func() *resource.RetryError {
var err error

output, err = conn.CreateListener(params)

if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeCertificateNotFoundException) {
return resource.RetryableError(err)
}

if err != nil {
return resource.NonRetryableError(err)
}

return nil
})

if tfresource.TimedOut(err) {
output, err = conn.CreateListener(params)
}

if err != nil {
return nil, err
}

if output == nil || len(output.Listeners) == 0 {
return nil, fmt.Errorf("error creating ELBv2 Listener: no listeners returned in response")
}

return output, nil
}

func expandLbListenerActions(l []interface{}) ([]*elbv2.Action, error) {
if len(l) == 0 {
return nil, nil
Expand Down
7 changes: 7 additions & 0 deletions internal/service/elbv2/listener_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package elbv2
import (
"errors"
"fmt"
"log"
"sort"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elbv2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
Expand Down Expand Up @@ -339,6 +341,11 @@ func dataSourceListenerRead(d *schema.ResourceData, meta interface{}) error {

tags, err := ListTags(conn, d.Id())

if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) {
log.Printf("[WARN] Unable to list tags for ELBv2 Listener %s: %s", d.Id(), err)
return nil
}

if err != nil {
return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err)
}
Expand Down
160 changes: 104 additions & 56 deletions internal/service/elbv2/listener_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,51 +510,36 @@ func resourceListenerRuleCreate(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("error creating LB Listener Rule for Listener (%s): %w", listenerArn, err)
}

var resp *elbv2.CreateRuleOutput
if v, ok := d.GetOk("priority"); ok {
var err error
params.Priority = aws.Int64(int64(v.(int)))
resp, err = conn.CreateRule(params)
if err != nil {
return fmt.Errorf("Error creating LB Listener Rule: %w", err)
}
} else {
var priority int64
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
var err error
priority, err = highestListenerRulePriority(conn, listenerArn)
if err != nil {
return resource.NonRetryableError(err)
}
params.Priority = aws.Int64(priority + 1)
resp, err = conn.CreateRule(params)
if err != nil {
if tfawserr.ErrMessageContains(err, elbv2.ErrCodePriorityInUseException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if tfresource.TimedOut(err) {
priority, err = highestListenerRulePriority(conn, listenerArn)
if err != nil {
return fmt.Errorf("Error getting highest listener rule priority: %w", err)
}
params.Priority = aws.Int64(priority + 1)
resp, err = conn.CreateRule(params)
}
if err != nil {
return fmt.Errorf("Error creating LB Listener Rule: %w", err)
}
resp, err := retryListenerRuleCreate(conn, d, params, listenerArn)

// Some partitions may not support tag-on-create
if params.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) {
log.Printf("[WARN] ELBv2 Listener Rule (%s) create failed (%s) with tags. Trying create without tags.", listenerArn, err)
params.Tags = nil
resp, err = retryListenerRuleCreate(conn, d, params, listenerArn)
}

if resp == nil || len(resp.Rules) == 0 {
return errors.New("Error creating LB Listener Rule: no rules returned in response")
if err != nil {
return fmt.Errorf("Error creating LB Listener Rule: %w", err)
}

d.SetId(aws.StringValue(resp.Rules[0].RuleArn))

// Post-create tagging supported in some partitions
if params.Tags == nil && len(tags) > 0 {
err := UpdateTags(conn, d.Id(), nil, tags)

if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) {
// if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be)
log.Printf("[WARN] error adding tags after create for ELBv2 Listener Rule (%s): %s", d.Id(), err)
return resourceListenerRuleRead(d, meta)
}

if err != nil {
return fmt.Errorf("error creating ELBv2 Listener Rule (%s) tags: %w", d.Id(), err)
}
}

return resourceListenerRuleRead(d, meta)
}

Expand Down Expand Up @@ -600,23 +585,6 @@ func resourceListenerRuleRead(d *schema.ResourceData, meta interface{}) error {

d.Set("arn", rule.RuleArn)

tags, err := ListTags(conn, d.Id())

if err != nil {
return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err)
}

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

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

if err := d.Set("tags_all", tags.Map()); err != nil {
return fmt.Errorf("error setting tags_all: %w", err)
}

// The listener arn isn't in the response but can be derived from the rule arn
d.Set("listener_arn", ListenerARNFromRuleARN(aws.StringValue(rule.RuleArn)))

Expand Down Expand Up @@ -796,6 +764,29 @@ func resourceListenerRuleRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error setting condition: %w", err)
}

// tags at the end because, if not supported, will skip the rest of Read
tags, err := ListTags(conn, d.Id())

if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) {
log.Printf("[WARN] Unable to list tags for ELBv2 Listener Rule %s: %s", d.Id(), err)
return nil
}

if err != nil {
return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err)
}

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

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

if err := d.Set("tags_all", tags.Map()); err != nil {
return fmt.Errorf("error setting tags_all: %w", err)
}

return nil
}

Expand Down Expand Up @@ -874,6 +865,12 @@ func resourceListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error
err = UpdateTags(conn, d.Id(), o, n)
}

// ISO partitions may not support tagging, giving error
if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) {
log.Printf("[WARN] Unable to update tags for ELBv2 Listener Rule %s: %s", d.Id(), err)
return resourceListenerRuleRead(d, meta)
}

if err != nil {
return fmt.Errorf("error updating LB (%s) tags: %w", d.Id(), err)
}
Expand All @@ -894,6 +891,57 @@ func resourceListenerRuleDelete(d *schema.ResourceData, meta interface{}) error
return nil
}

func retryListenerRuleCreate(conn *elbv2.ELBV2, d *schema.ResourceData, params *elbv2.CreateRuleInput, listenerARN string) (*elbv2.CreateRuleOutput, error) {
var resp *elbv2.CreateRuleOutput
if v, ok := d.GetOk("priority"); ok {
var err error
params.Priority = aws.Int64(int64(v.(int)))
resp, err = conn.CreateRule(params)

if err != nil {
return nil, err
}
} else {
var priority int64

err := resource.Retry(5*time.Minute, func() *resource.RetryError {
var err error
priority, err = highestListenerRulePriority(conn, listenerARN)
if err != nil {
return resource.NonRetryableError(err)
}
params.Priority = aws.Int64(priority + 1)
resp, err = conn.CreateRule(params)
if err != nil {
if tfawserr.ErrMessageContains(err, elbv2.ErrCodePriorityInUseException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})

if tfresource.TimedOut(err) {
priority, err = highestListenerRulePriority(conn, listenerARN)
if err != nil {
return nil, fmt.Errorf("getting highest listener rule (%s) priority: %w", listenerARN, err)
}
params.Priority = aws.Int64(priority + 1)
resp, err = conn.CreateRule(params)
}

if err != nil {
return nil, err
}
}

if resp == nil || len(resp.Rules) == 0 {
return nil, fmt.Errorf("creating LB Listener Rule (%s): no rules returned in response", listenerARN)
}

return resp, nil
}

func validListenerRulePriority(v interface{}, k string) (ws []string, errors []error) {
value := v.(int)
if value < listenerRulePriorityMin || (value > listenerRulePriorityMax && value != listenerRulePriorityDefault) {
Expand Down
Loading

0 comments on commit 27d127b

Please sign in to comment.