-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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/aws_globalaccelerator_listener: Add aws_globalaccelerator_listener resource #7003
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,270 @@ | ||||||||||
package aws | ||||||||||
|
||||||||||
import ( | ||||||||||
"fmt" | ||||||||||
"log" | ||||||||||
"strings" | ||||||||||
"time" | ||||||||||
|
||||||||||
"github.com/aws/aws-sdk-go/aws" | ||||||||||
"github.com/aws/aws-sdk-go/service/globalaccelerator" | ||||||||||
|
||||||||||
"github.com/hashicorp/terraform/helper/resource" | ||||||||||
"github.com/hashicorp/terraform/helper/schema" | ||||||||||
"github.com/hashicorp/terraform/helper/validation" | ||||||||||
) | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListener() *schema.Resource { | ||||||||||
return &schema.Resource{ | ||||||||||
Create: resourceAwsGlobalAcceleratorListenerCreate, | ||||||||||
Read: resourceAwsGlobalAcceleratorListenerRead, | ||||||||||
Update: resourceAwsGlobalAcceleratorListenerUpdate, | ||||||||||
Delete: resourceAwsGlobalAcceleratorListenerDelete, | ||||||||||
|
||||||||||
Importer: &schema.ResourceImporter{ | ||||||||||
State: schema.ImportStatePassthrough, | ||||||||||
}, | ||||||||||
|
||||||||||
Timeouts: &schema.ResourceTimeout{ | ||||||||||
Create: schema.DefaultTimeout(5 * time.Minute), | ||||||||||
Update: schema.DefaultTimeout(5 * time.Minute), | ||||||||||
Delete: schema.DefaultTimeout(5 * time.Minute), | ||||||||||
}, | ||||||||||
|
||||||||||
Schema: map[string]*schema.Schema{ | ||||||||||
"accelerator_arn": { | ||||||||||
Type: schema.TypeString, | ||||||||||
Required: true, | ||||||||||
ForceNew: true, | ||||||||||
}, | ||||||||||
"client_affinity": { | ||||||||||
Type: schema.TypeString, | ||||||||||
Optional: true, | ||||||||||
Computed: true, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should switch this to
Suggested change
|
||||||||||
ValidateFunc: validation.StringInSlice([]string{ | ||||||||||
globalaccelerator.ClientAffinityNone, | ||||||||||
globalaccelerator.ClientAffinitySourceIp, | ||||||||||
}, false), | ||||||||||
}, | ||||||||||
"protocol": { | ||||||||||
Type: schema.TypeString, | ||||||||||
Required: true, | ||||||||||
ValidateFunc: validation.StringInSlice([]string{ | ||||||||||
globalaccelerator.ProtocolTcp, | ||||||||||
globalaccelerator.ProtocolUdp, | ||||||||||
}, false), | ||||||||||
}, | ||||||||||
"port_range": { | ||||||||||
Type: schema.TypeList, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the ordering matter for these? If not, we should use |
||||||||||
Required: true, | ||||||||||
MinItems: 1, | ||||||||||
MaxItems: 10, | ||||||||||
Elem: &schema.Resource{ | ||||||||||
Schema: map[string]*schema.Schema{ | ||||||||||
"from_port": { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add plan-time validation for this attribute and ValidateFunc: validation.IntBetween(1, 65535), |
||||||||||
Type: schema.TypeInt, | ||||||||||
Optional: true, | ||||||||||
}, | ||||||||||
"to_port": { | ||||||||||
Type: schema.TypeInt, | ||||||||||
Optional: true, | ||||||||||
}, | ||||||||||
}, | ||||||||||
}, | ||||||||||
}, | ||||||||||
}, | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerCreate(d *schema.ResourceData, meta interface{}) error { | ||||||||||
conn := meta.(*AWSClient).globalacceleratorconn | ||||||||||
|
||||||||||
opts := &globalaccelerator.CreateListenerInput{ | ||||||||||
AcceleratorArn: aws.String(d.Get("accelerator_arn").(string)), | ||||||||||
IdempotencyToken: aws.String(resource.UniqueId()), | ||||||||||
Protocol: aws.String(d.Get("protocol").(string)), | ||||||||||
PortRanges: resourceAwsGlobalAcceleratorListenerExpandPortRanges(d.Get("port_range").([]interface{})), | ||||||||||
} | ||||||||||
|
||||||||||
if v, ok := d.GetOk("client_affinity"); ok { | ||||||||||
opts.ClientAffinity = aws.String(v.(string)) | ||||||||||
} | ||||||||||
|
||||||||||
log.Printf("[DEBUG] Create Global Accelerator listener: %s", opts) | ||||||||||
|
||||||||||
resp, err := conn.CreateListener(opts) | ||||||||||
if err != nil { | ||||||||||
return fmt.Errorf("Error creating Global Accelerator listener: %s", err) | ||||||||||
} | ||||||||||
|
||||||||||
d.SetId(*resp.Listener.ListenerArn) | ||||||||||
|
||||||||||
// Creating a listener triggers the accelerator to change status to InPending | ||||||||||
stateConf := &resource.StateChangeConf{ | ||||||||||
Pending: []string{globalaccelerator.AcceleratorStatusInProgress}, | ||||||||||
Target: []string{globalaccelerator.AcceleratorStatusDeployed}, | ||||||||||
Refresh: resourceAwsGlobalAcceleratorAcceleratorStateRefreshFunc(conn, d.Get("accelerator_arn").(string)), | ||||||||||
Timeout: d.Timeout(schema.TimeoutCreate), | ||||||||||
} | ||||||||||
|
||||||||||
log.Printf("[DEBUG] Waiting for Global Accelerator listener (%s) availability", d.Id()) | ||||||||||
_, err = stateConf.WaitForState() | ||||||||||
if err != nil { | ||||||||||
return fmt.Errorf("Error waiting for Global Accelerator listener (%s) availability: %s", d.Id(), err) | ||||||||||
} | ||||||||||
|
||||||||||
return resourceAwsGlobalAcceleratorListenerRead(d, meta) | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerRead(d *schema.ResourceData, meta interface{}) error { | ||||||||||
conn := meta.(*AWSClient).globalacceleratorconn | ||||||||||
|
||||||||||
listener, err := resourceAwsGlobalAcceleratorListenerRetrieve(conn, d.Id()) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return fmt.Errorf("Error reading Global Accelerator listener: %s", err) | ||||||||||
} | ||||||||||
|
||||||||||
if listener == nil { | ||||||||||
log.Printf("[WARN] Global Accelerator listener (%s) not found, removing from state", d.Id()) | ||||||||||
d.SetId("") | ||||||||||
return nil | ||||||||||
} | ||||||||||
|
||||||||||
acceleratorArn, err := resourceAwsGlobalAcceleratorListenerParseAcceleratorArn(d.Id()) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return err | ||||||||||
} | ||||||||||
|
||||||||||
d.Set("accelerator_arn", acceleratorArn) | ||||||||||
d.Set("client_affinity", listener.ClientAffinity) | ||||||||||
d.Set("protocol", listener.Protocol) | ||||||||||
d.Set("port_range", resourceAwsGlobalAcceleratorListenerFlattenPortRanges(listener.PortRanges)) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When using
Suggested change
|
||||||||||
|
||||||||||
return nil | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerParseAcceleratorArn(listenerArn string) (string, error) { | ||||||||||
parts := strings.Split(listenerArn, "/") | ||||||||||
if len(parts) < 4 { | ||||||||||
return "", fmt.Errorf("Unable to parse accelerator ARN from %s", listenerArn) | ||||||||||
} | ||||||||||
return strings.Join(parts[0:2], "/"), nil | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerExpandPortRanges(portRanges []interface{}) []*globalaccelerator.PortRange { | ||||||||||
out := make([]*globalaccelerator.PortRange, len(portRanges)) | ||||||||||
|
||||||||||
for i, raw := range portRanges { | ||||||||||
portRange := raw.(map[string]interface{}) | ||||||||||
m := globalaccelerator.PortRange{} | ||||||||||
|
||||||||||
m.FromPort = aws.Int64(int64(portRange["from_port"].(int))) | ||||||||||
m.ToPort = aws.Int64(int64(portRange["to_port"].(int))) | ||||||||||
|
||||||||||
out[i] = &m | ||||||||||
} | ||||||||||
|
||||||||||
return out | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerFlattenPortRanges(portRanges []*globalaccelerator.PortRange) []interface{} { | ||||||||||
out := make([]interface{}, len(portRanges)) | ||||||||||
|
||||||||||
for i, portRange := range portRanges { | ||||||||||
m := make(map[string]interface{}) | ||||||||||
|
||||||||||
m["from_port"] = *portRange.FromPort | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To prevent potential panics we should use the AWS Go SDK provided helpers with FromPort and ToPort: m["from_port"] = aws.Int64Value(portRange.FromPort)
m["to_port"] = aws.Int64Value(portRange.ToPort) |
||||||||||
m["to_port"] = *portRange.ToPort | ||||||||||
|
||||||||||
out[i] = m | ||||||||||
} | ||||||||||
|
||||||||||
log.Printf("[DEBUG] Flatten port_range: %s", out) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Extraneous logging can be removed. 👍 |
||||||||||
return out | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerRetrieve(conn *globalaccelerator.GlobalAccelerator, listenerArn string) (*globalaccelerator.Listener, error) { | ||||||||||
resp, err := conn.DescribeListener(&globalaccelerator.DescribeListenerInput{ | ||||||||||
ListenerArn: aws.String(listenerArn), | ||||||||||
}) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
if isAWSErr(err, globalaccelerator.ErrCodeListenerNotFoundException, "") { | ||||||||||
return nil, nil | ||||||||||
} | ||||||||||
return nil, err | ||||||||||
} | ||||||||||
|
||||||||||
return resp.Listener, nil | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerUpdate(d *schema.ResourceData, meta interface{}) error { | ||||||||||
conn := meta.(*AWSClient).globalacceleratorconn | ||||||||||
|
||||||||||
opts := &globalaccelerator.UpdateListenerInput{ | ||||||||||
ListenerArn: aws.String(d.Id()), | ||||||||||
Protocol: aws.String(d.Get("protocol").(string)), | ||||||||||
PortRanges: resourceAwsGlobalAcceleratorListenerExpandPortRanges(d.Get("port_range").([]interface{})), | ||||||||||
} | ||||||||||
|
||||||||||
if v, ok := d.GetOk("client_affinity"); ok { | ||||||||||
opts.ClientAffinity = aws.String(v.(string)) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we are switching |
||||||||||
} | ||||||||||
|
||||||||||
log.Printf("[DEBUG] Update Global Accelerator listener: %s", opts) | ||||||||||
|
||||||||||
_, err := conn.UpdateListener(opts) | ||||||||||
if err != nil { | ||||||||||
return fmt.Errorf("Error updating Global Accelerator listener: %s", err) | ||||||||||
} | ||||||||||
|
||||||||||
// Creating a listener triggers the accelerator to change status to InPending | ||||||||||
stateConf := &resource.StateChangeConf{ | ||||||||||
Pending: []string{globalaccelerator.AcceleratorStatusInProgress}, | ||||||||||
Target: []string{globalaccelerator.AcceleratorStatusDeployed}, | ||||||||||
Refresh: resourceAwsGlobalAcceleratorAcceleratorStateRefreshFunc(conn, d.Get("accelerator_arn").(string)), | ||||||||||
Timeout: d.Timeout(schema.TimeoutUpdate), | ||||||||||
} | ||||||||||
|
||||||||||
log.Printf("[DEBUG] Waiting for Global Accelerator listener (%s) availability", d.Id()) | ||||||||||
_, err = stateConf.WaitForState() | ||||||||||
if err != nil { | ||||||||||
return fmt.Errorf("Error waiting for Global Accelerator listener (%s) availability: %s", d.Id(), err) | ||||||||||
} | ||||||||||
|
||||||||||
return resourceAwsGlobalAcceleratorListenerRead(d, meta) | ||||||||||
} | ||||||||||
|
||||||||||
func resourceAwsGlobalAcceleratorListenerDelete(d *schema.ResourceData, meta interface{}) error { | ||||||||||
conn := meta.(*AWSClient).globalacceleratorconn | ||||||||||
|
||||||||||
opts := &globalaccelerator.DeleteListenerInput{ | ||||||||||
ListenerArn: aws.String(d.Id()), | ||||||||||
} | ||||||||||
|
||||||||||
_, err := conn.DeleteListener(opts) | ||||||||||
if err != nil { | ||||||||||
if isAWSErr(err, globalaccelerator.ErrCodeListenerNotFoundException, "") { | ||||||||||
return nil | ||||||||||
} | ||||||||||
return fmt.Errorf("Error deleting Global Accelerator listener: %s", err) | ||||||||||
} | ||||||||||
|
||||||||||
// Deleting a listener triggers the accelerator to change status to InPending | ||||||||||
stateConf := &resource.StateChangeConf{ | ||||||||||
Pending: []string{globalaccelerator.AcceleratorStatusInProgress}, | ||||||||||
Target: []string{globalaccelerator.AcceleratorStatusDeployed}, | ||||||||||
Refresh: resourceAwsGlobalAcceleratorAcceleratorStateRefreshFunc(conn, d.Get("accelerator_arn").(string)), | ||||||||||
Timeout: d.Timeout(schema.TimeoutDelete), | ||||||||||
} | ||||||||||
|
||||||||||
log.Printf("[DEBUG] Waiting for Global Accelerator listener (%s) deletion", d.Id()) | ||||||||||
_, err = stateConf.WaitForState() | ||||||||||
if err != nil { | ||||||||||
return fmt.Errorf("Error waiting for Global Accelerator listener (%s) deletion: %s", d.Id(), err) | ||||||||||
} | ||||||||||
|
||||||||||
return nil | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should likely remove these customizable timeouts since: