-
Notifications
You must be signed in to change notification settings - Fork 9.1k
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
New Resource: aws_dx_private_virtual_interface #3253
Changes from 6 commits
90488e0
d7bd808
6a1f5a4
e3b13ce
4b7d622
2b93824
173e1d9
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,168 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/arn" | ||
"github.com/aws/aws-sdk-go/service/directconnect" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*directconnect.VirtualInterface, error) { | ||
resp, state, err := dxVirtualInterfaceStateRefresh(conn, id)() | ||
if err != nil { | ||
return nil, fmt.Errorf("Error reading Direct Connect virtual interface: %s", err) | ||
} | ||
if state == directconnect.VirtualInterfaceStateDeleted { | ||
return nil, nil | ||
} | ||
|
||
return resp.(*directconnect.VirtualInterface), nil | ||
} | ||
|
||
func dxVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).dxconn | ||
|
||
arn := arn.ARN{ | ||
Partition: meta.(*AWSClient).partition, | ||
Region: meta.(*AWSClient).region, | ||
Service: "directconnect", | ||
AccountID: meta.(*AWSClient).accountid, | ||
Resource: fmt.Sprintf("dxvif/%s", d.Id()), | ||
}.String() | ||
if err := setTagsDX(conn, d, arn); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func dxVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).dxconn | ||
|
||
log.Printf("[DEBUG] Deleting Direct Connect virtual interface: %s", d.Id()) | ||
_, err := conn.DeleteVirtualInterface(&directconnect.DeleteVirtualInterfaceInput{ | ||
VirtualInterfaceId: aws.String(d.Id()), | ||
}) | ||
if err != nil { | ||
if isAWSErr(err, "DirectConnectClientException", "does not exist") { | ||
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. Minor nitpick: looks like the SDK has an available constant for this: 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. 👍 |
||
return nil | ||
} | ||
return fmt.Errorf("Error deleting Direct Connect virtual interface: %s", err) | ||
} | ||
|
||
deleteStateConf := &resource.StateChangeConf{ | ||
Pending: []string{ | ||
directconnect.VirtualInterfaceStateAvailable, | ||
directconnect.VirtualInterfaceStateConfirming, | ||
directconnect.VirtualInterfaceStateDeleting, | ||
directconnect.VirtualInterfaceStateDown, | ||
directconnect.VirtualInterfaceStatePending, | ||
directconnect.VirtualInterfaceStateRejected, | ||
directconnect.VirtualInterfaceStateVerifying, | ||
}, | ||
Target: []string{ | ||
directconnect.VirtualInterfaceStateDeleted, | ||
}, | ||
Refresh: dxVirtualInterfaceStateRefresh(conn, d.Id()), | ||
Timeout: d.Timeout(schema.TimeoutDelete), | ||
Delay: 10 * time.Second, | ||
MinTimeout: 5 * time.Second, | ||
} | ||
_, err = deleteStateConf.WaitForState() | ||
if err != nil { | ||
return fmt.Errorf("Error waiting for Direct Connect virtual interface (%s) to be deleted: %s", d.Id(), err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func dxVirtualInterfaceStateRefresh(conn *directconnect.DirectConnect, vifId string) resource.StateRefreshFunc { | ||
return func() (interface{}, string, error) { | ||
resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ | ||
VirtualInterfaceId: aws.String(vifId), | ||
}) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
n := len(resp.VirtualInterfaces) | ||
switch n { | ||
case 0: | ||
return "", directconnect.VirtualInterfaceStateDeleted, nil | ||
|
||
case 1: | ||
vif := resp.VirtualInterfaces[0] | ||
return vif, aws.StringValue(vif.VirtualInterfaceState), nil | ||
|
||
default: | ||
return nil, "", fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, vifId) | ||
} | ||
} | ||
} | ||
|
||
func dxVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect, pending, target []string) error { | ||
stateConf := &resource.StateChangeConf{ | ||
Pending: pending, | ||
Target: target, | ||
Refresh: dxVirtualInterfaceStateRefresh(conn, d.Id()), | ||
Timeout: d.Timeout(schema.TimeoutCreate), | ||
Delay: 10 * time.Second, | ||
MinTimeout: 5 * time.Second, | ||
} | ||
if _, err := stateConf.WaitForState(); err != nil { | ||
return fmt.Errorf("Error waiting for Direct Connect virtual interface (%s) to become available: %s", d.Id(), err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Attributes common to public VIFs and creator side of hosted public VIFs. | ||
func dxPublicVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { | ||
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 generally prefer this logic to be duplicated across resources as necessary for long-term maintainability. 👍 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. Yes, I was thinking of that after removing |
||
if err := dxVirtualInterfaceAttributes(d, meta, vif); err != nil { | ||
return err | ||
} | ||
d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)) | ||
|
||
return nil | ||
} | ||
|
||
// Attributes common to private VIFs and creator side of hosted private VIFs. | ||
func dxPrivateVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { | ||
return dxVirtualInterfaceAttributes(d, meta, vif) | ||
} | ||
|
||
// Attributes common to public/private VIFs and creator side of hosted public/private VIFs. | ||
func dxVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { | ||
if err := dxVirtualInterfaceArnAttribute(d, meta); err != nil { | ||
return err | ||
} | ||
|
||
d.Set("connection_id", vif.ConnectionId) | ||
d.Set("name", vif.VirtualInterfaceName) | ||
d.Set("vlan", vif.Vlan) | ||
d.Set("bgp_asn", vif.Asn) | ||
d.Set("bgp_auth_key", vif.AuthKey) | ||
d.Set("address_family", vif.AddressFamily) | ||
d.Set("customer_address", vif.CustomerAddress) | ||
d.Set("amazon_address", vif.AmazonAddress) | ||
|
||
return nil | ||
} | ||
|
||
func dxVirtualInterfaceArnAttribute(d *schema.ResourceData, meta interface{}) error { | ||
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. Potential nitpick: this indirection to save two lines of code seems extraneous when it can just be duplicated to the relevant resources 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. I'll consolidate and inline all the attribute setting code directly into the resource's |
||
arn := arn.ARN{ | ||
Partition: meta.(*AWSClient).partition, | ||
Region: meta.(*AWSClient).region, | ||
Service: "directconnect", | ||
AccountID: meta.(*AWSClient).accountid, | ||
Resource: fmt.Sprintf("dxvif/%s", d.Id()), | ||
}.String() | ||
d.Set("arn", arn) | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/directconnect" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/helper/validation" | ||
) | ||
|
||
func resourceAwsDxPrivateVirtualInterface() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsDxPrivateVirtualInterfaceCreate, | ||
Read: resourceAwsDxPrivateVirtualInterfaceRead, | ||
Update: resourceAwsDxPrivateVirtualInterfaceUpdate, | ||
Delete: resourceAwsDxPrivateVirtualInterfaceDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"arn": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"connection_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"vpn_gateway_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
ConflictsWith: []string{"dx_gateway_id"}, | ||
}, | ||
"dx_gateway_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
ConflictsWith: []string{"vpn_gateway_id"}, | ||
}, | ||
"vlan": { | ||
Type: schema.TypeInt, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.IntBetween(1, 4094), | ||
}, | ||
"bgp_asn": { | ||
Type: schema.TypeInt, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"bgp_auth_key": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
ForceNew: true, | ||
}, | ||
"address_family": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), | ||
}, | ||
"customer_address": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
ForceNew: true, | ||
}, | ||
"amazon_address": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
ForceNew: true, | ||
}, | ||
"tags": tagsSchema(), | ||
}, | ||
|
||
Timeouts: &schema.ResourceTimeout{ | ||
Create: schema.DefaultTimeout(10 * time.Minute), | ||
Delete: schema.DefaultTimeout(10 * time.Minute), | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsDxPrivateVirtualInterfaceCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).dxconn | ||
|
||
vgwIdRaw, vgwOk := d.GetOk("vpn_gateway_id") | ||
dxgwIdRaw, dxgwOk := d.GetOk("dx_gateway_id") | ||
if vgwOk == dxgwOk { | ||
return fmt.Errorf( | ||
"One of ['vpn_gateway_id', 'dx_gateway_id'] must be set to create a Direct Connect private virtual interface") | ||
} | ||
|
||
req := &directconnect.CreatePrivateVirtualInterfaceInput{ | ||
ConnectionId: aws.String(d.Get("connection_id").(string)), | ||
NewPrivateVirtualInterface: &directconnect.NewPrivateVirtualInterface{ | ||
VirtualInterfaceName: aws.String(d.Get("name").(string)), | ||
Vlan: aws.Int64(int64(d.Get("vlan").(int))), | ||
Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), | ||
AddressFamily: aws.String(d.Get("address_family").(string)), | ||
}, | ||
} | ||
if vgwOk { | ||
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. For easier module support should this, and the other 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. 👍 |
||
req.NewPrivateVirtualInterface.VirtualGatewayId = aws.String(vgwIdRaw.(string)) | ||
} | ||
if dxgwOk { | ||
req.NewPrivateVirtualInterface.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) | ||
} | ||
if v, ok := d.GetOk("bgp_auth_key"); ok { | ||
req.NewPrivateVirtualInterface.AuthKey = aws.String(v.(string)) | ||
} | ||
if v, ok := d.GetOk("customer_address"); ok { | ||
req.NewPrivateVirtualInterface.CustomerAddress = aws.String(v.(string)) | ||
} | ||
if v, ok := d.GetOk("amazon_address"); ok { | ||
req.NewPrivateVirtualInterface.AmazonAddress = aws.String(v.(string)) | ||
} | ||
|
||
log.Printf("[DEBUG] Creating Direct Connect private virtual interface: %#v", req) | ||
resp, err := conn.CreatePrivateVirtualInterface(req) | ||
if err != nil { | ||
return fmt.Errorf("Error creating Direct Connect private virtual interface: %s", err.Error()) | ||
} | ||
|
||
d.SetId(aws.StringValue(resp.VirtualInterfaceId)) | ||
|
||
if err := dxPrivateVirtualInterfaceWaitUntilAvailable(d, conn); err != nil { | ||
return err | ||
} | ||
|
||
return resourceAwsDxPrivateVirtualInterfaceUpdate(d, meta) | ||
} | ||
|
||
func resourceAwsDxPrivateVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).dxconn | ||
|
||
vif, err := dxVirtualInterfaceRead(d.Id(), conn) | ||
if err != nil { | ||
return err | ||
} | ||
if vif == nil { | ||
log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
if err := dxPrivateVirtualInterfaceAttributes(d, meta, vif); err != nil { | ||
return err | ||
} | ||
d.Set("vpn_gateway_id", vif.VirtualGatewayId) | ||
d.Set("dx_gateway_id", vif.DirectConnectGatewayId) | ||
if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsDxPrivateVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { | ||
if err := dxVirtualInterfaceUpdate(d, meta); err != nil { | ||
return err | ||
} | ||
|
||
return resourceAwsDxPrivateVirtualInterfaceRead(d, meta) | ||
} | ||
|
||
func resourceAwsDxPrivateVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { | ||
return dxVirtualInterfaceDelete(d, meta) | ||
} | ||
|
||
func dxPrivateVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect) error { | ||
return dxVirtualInterfaceWaitUntilAvailable( | ||
d, | ||
conn, | ||
[]string{ | ||
directconnect.VirtualInterfaceStatePending, | ||
}, | ||
[]string{ | ||
directconnect.VirtualInterfaceStateAvailable, | ||
directconnect.VirtualInterfaceStateDown, | ||
}) | ||
} |
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 prefer to create the
arn
manually once during create or read, set it in the Terraform state viad.Set("arn", ...)
, then used.Get("arn").(string)
or pass by value as necessary.Once that's done, this function basically becomes nothing and the
setTagsDX()
bits can be duplicated where necessary to remove thedxVirtualInterfaceUpdate
function. 👍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.
Yes, I'll
Set
the ARN after creation, once we have theId
andGet
it elsewhere.