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

elbv2: ISO-friendly tagging #22551

Merged
merged 9 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
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