Skip to content

Commit

Permalink
megaport_aws_vxc: implement updates
Browse files Browse the repository at this point in the history
Signed-off-by: Dimitrios Karagiannis <[email protected]>
  • Loading branch information
alkar committed Jan 17, 2020
1 parent 243f199 commit 719e4bc
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 74 deletions.
12 changes: 8 additions & 4 deletions examples/megaport_aws_vxc_basic_update/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ resource "megaport_aws_vxc" "foo" {
}

b_end {
product_uid = data.megaport_partner_port.aws.id
aws_account_id = "{{ .aws_account_id }}"
customer_asn = {{ .customer_asn }}
type = "private"
product_uid = data.megaport_partner_port.aws.id
aws_account_id = "{{ .aws_account_id }}"
aws_connection_name = "{{ .uid }}"
aws_ip_address = "{{ .aws_ip_address }}"
bgp_auth_key = "{{ .uid }}"
customer_asn = {{ .customer_asn }}
customer_ip_address = "{{ .customer_ip_address }}"
type = "private"
}
}
27 changes: 16 additions & 11 deletions megaport/api/vxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,13 @@ func (v *PrivateVxcCreateInput) toPayload() ([]byte, error) {
}

type vxcUpdatePayload struct {
AEndVlan *uint64 `json:"aEndVlan,omitempty"`
BEndVlan *uint64 `json:"bEndVlan,omitempty"`
CostCentre *string `json:"costCentre,omitempty"`
Name *string `json:"name,omitempty"`
RateLimit *uint64 `json:"rateLimit,omitempty"`
AEndVlan *uint64 `json:"aEndVlan,omitempty"`
BEndVlan *uint64 `json:"bEndVlan,omitempty"`
CostCentre *string `json:"costCentre,omitempty"`
Name *string `json:"name,omitempty"`
BEndConfig interface{} `json:"bEndConfig,omitempty"`
RateLimit *uint64 `json:"rateLimit,omitempty"`
// SecondaryName *string `json:"secondaryName,omitempty"`
}

type PrivateVxcUpdateInput struct {
Expand Down Expand Up @@ -219,15 +221,16 @@ func (v *PartnerConfigAWS) toPayload() interface{} {
}

type vxcCreatePayloadPartnerConfigAWS struct {
// AmazonAsn *uint64 `json:",omitempty"`
AmazonIpAddress *string `json:"amazonIpAddress,omitempty"`
Asn *uint64 `json:"asn,omitempty"`
AuthKey *string `json:"authKey,omitempty"`
ConnectType *string `json:"connectType,omitempty"`
// AmazonAsn *uint64 `json:",omitempty"`
AmazonIpAddress *string `json:"amazonIpAddress,omitempty"`
Asn *uint64 `json:"asn,omitempty"`
AuthKey *string `json:"authKey,omitempty"`
ConnectType *string `json:"connectType,omitempty"`
// Complete *bool `json:"complete,omitempty"`
CustomerIpAddress *string `json:"customerIpAddress,omitempty"`
Name *string `json:"name,omitempty"`
OwnerAccount *string `json:"ownerAccount,omitempty"`
// Prefixes *string `json:",omitempty"`
// Prefixes *string `json:",omitempty"`
Type *string `json:"type,omitempty"`
}

Expand Down Expand Up @@ -270,6 +273,7 @@ type CloudVxcUpdateInput struct {
InvoiceReference *string
Name *string
ProductUid *string
PartnerConfig PartnerConfig
RateLimit *uint64
VlanA *uint64
}
Expand All @@ -283,6 +287,7 @@ func (v *CloudVxcUpdateInput) toPayload() ([]byte, error) {
AEndVlan: v.VlanA,
CostCentre: v.InvoiceReference,
Name: v.Name,
BEndConfig: v.PartnerConfig.toPayload(),
RateLimit: v.RateLimit,
}
return json.Marshal(payload)
Expand Down
8 changes: 8 additions & 0 deletions megaport/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,11 @@ func isResourceDeleted(provisioningStatus string) bool {
return false
}
}

func compareNillableStrings(a *string, b string) bool {
return a == nil || *a == b
}

func compareNillableUints(a *uint64, b uint64) bool {
return a == nil || *a == b
}
121 changes: 72 additions & 49 deletions megaport/resource_megaport_aws_vxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,27 @@ func flattenVxcEndAws(configProductUid string, v api.ProductAssociatedVxcEnd, r
}}
}

func expandVxcEndAws(e map[string]interface{}) *api.PartnerConfigAWS {
pc := &api.PartnerConfigAWS{
AWSAccountID: api.String(e["aws_account_id"]),
CustomerASN: api.Uint64FromInt(e["customer_asn"]),
Type: api.String(e["type"]),
}
if v := e["aws_connection_name"]; v != "" {
pc.AWSConnectionName = api.String(v)
}
if v := e["aws_ip_address"]; v != "" {
pc.AmazonIPAddress = api.String(v)
}
if v := e["bgp_auth_key"]; v != "" {
pc.BGPAuthKey = api.String(v)
}
if v := e["customer_ip_address"]; v != "" {
pc.CustomerIPAddress = api.String(v)
}
return pc
}

func resourceMegaportAwsVxcRead(d *schema.ResourceData, m interface{}) error {
cfg := m.(*Config)
p, err := cfg.Client.GetCloudVxc(d.Id())
Expand Down Expand Up @@ -149,41 +170,24 @@ func resourceMegaportAwsVxcCreate(d *schema.ResourceData, m interface{}) error {
a := d.Get("a_end").([]interface{})[0].(map[string]interface{})
b := d.Get("b_end").([]interface{})[0].(map[string]interface{})
input := &api.CloudVxcCreateInput{
ProductUidA: api.String(a["product_uid"]),
ProductUidB: api.String(b["product_uid"]),
Name: api.String(d.Get("name")),
RateLimit: api.Uint64FromInt(d.Get("rate_limit")),
ProductUidA: api.String(a["product_uid"]),
ProductUidB: api.String(b["product_uid"]),
Name: api.String(d.Get("name")),
PartnerConfig: expandVxcEndAws(b),
RateLimit: api.Uint64FromInt(d.Get("rate_limit")),
}
if v, ok := d.GetOk("invoice_reference"); ok {
input.InvoiceReference = api.String(v)
}
if v := a["vlan"]; v != 0 {
input.VlanA = api.Uint64FromInt(a["vlan"])
}
inputPartnerConfig := &api.PartnerConfigAWS{
AWSAccountID: api.String(b["aws_account_id"]),
CustomerASN: api.Uint64FromInt(b["customer_asn"]),
Type: api.String(b["type"]),
}
if v := b["aws_connection_name"]; v != "" {
inputPartnerConfig.AWSConnectionName = api.String(v)
}
if v := b["amazon_ip_address"]; v != "" {
inputPartnerConfig.AmazonIPAddress = api.String(v)
}
if v := b["bgp_auth_key"]; v != "" {
inputPartnerConfig.BGPAuthKey = api.String(v)
}
if v := b["customer_ip_address"]; v != "" {
inputPartnerConfig.CustomerIPAddress = api.String(v)
}
input.PartnerConfig = inputPartnerConfig
uid, err := cfg.Client.CreateCloudVxc(input)
if err != nil {
return err
}
d.SetId(*uid)
if err := waitUntilVxcIsConfigured(cfg.Client, *uid, 5*time.Minute); err != nil {
if err := waitUntilAwsVxcIsConfigured(cfg.Client, *uid, 5*time.Minute); err != nil {
return err
}
return resourceMegaportAwsVxcRead(d, m)
Expand All @@ -192,21 +196,22 @@ func resourceMegaportAwsVxcCreate(d *schema.ResourceData, m interface{}) error {
func resourceMegaportAwsVxcUpdate(d *schema.ResourceData, m interface{}) error {
cfg := m.(*Config)
a := d.Get("a_end").([]interface{})[0].(map[string]interface{})
//b := d.Get("b_end").([]interface{})[0].(map[string]interface{}) // TODO
b := d.Get("b_end").([]interface{})[0].(map[string]interface{})
input := &api.CloudVxcUpdateInput{
InvoiceReference: api.String(d.Get("invoice_reference")),
Name: api.String(d.Get("name")),
PartnerConfig: expandVxcEndAws(b),
ProductUid: api.String(d.Id()),
RateLimit: api.Uint64FromInt(d.Get("rate_limit")),
VlanA: api.Uint64FromInt(a["vlan"]),
}
if err := cfg.Client.UpdateCloudVxc(input); err != nil {
return err
}
if err := waitUntilVxcIsConfigured(cfg.Client, d.Id(), 5*time.Minute); err != nil {
if err := waitUntilAwsVxcIsConfigured(cfg.Client, d.Id(), 5*time.Minute); err != nil {
return err
}
if err := waitUntilVxcIsUpdated(cfg.Client, input, 5*time.Minute); err != nil {
if err := waitUntilAwsVxcIsUpdated(cfg.Client, input, 5*time.Minute); err != nil {
return err
}
return resourceMegaportAwsVxcRead(d, m)
Expand All @@ -224,11 +229,21 @@ func resourceMegaportAwsVxcDelete(d *schema.ResourceData, m interface{}) error {
return nil
}

func waitUntilVxcIsConfigured(client *api.Client, productUid string, timeout time.Duration) error {
func waitUntilAwsVxcIsConfigured(client *api.Client, productUid string, timeout time.Duration) error {
scc := &resource.StateChangeConf{
Pending: []string{api.ProductStatusDeployable},
Target: []string{api.ProductStatusConfigured, api.ProductStatusLive},
Refresh: resourceMegaportAwsVxcStateRefreshFunc(client, productUid),
Pending: []string{api.ProductStatusDeployable},
Target: []string{api.ProductStatusConfigured, api.ProductStatusLive},
Refresh: func() (interface{}, string, error) {
v, err := client.GetCloudVxc(productUid) // TODO we can probably use this for any kind of VXC
if err != nil {
log.Printf("Error retrieving VXC while waiting for setup to finish: %v", err)
return nil, "", err
}
if v == nil {
return nil, "", nil
}
return v, v.ProvisioningStatus, nil
},
Timeout: timeout,
MinTimeout: 10 * time.Second,
Delay: 5 * time.Second,
Expand All @@ -238,21 +253,7 @@ func waitUntilVxcIsConfigured(client *api.Client, productUid string, timeout tim
return err
}

func resourceMegaportAwsVxcStateRefreshFunc(client *api.Client, uid string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
v, err := client.GetCloudVxc(uid)
if err != nil {
log.Printf("Error retrieving VXC while waiting for setup to finish: %v", err)
return nil, "", err
}
if v == nil {
return nil, "", nil
}
return v, v.ProvisioningStatus, nil
}
}

func waitUntilVxcIsUpdated(client *api.Client, input *api.CloudVxcUpdateInput, timeout time.Duration) error {
func waitUntilAwsVxcIsUpdated(client *api.Client, input *api.CloudVxcUpdateInput, timeout time.Duration) error {
scc := &resource.StateChangeConf{
Pending: []string{"PENDING"},
Target: []string{"UPDATED"},
Expand All @@ -265,16 +266,38 @@ func waitUntilVxcIsUpdated(client *api.Client, input *api.CloudVxcUpdateInput, t
if v == nil {
return nil, "", nil
}
if v.CostCentre != *input.InvoiceReference {
if !compareNillableStrings(input.InvoiceReference, v.CostCentre) {
return nil, "PENDING", nil
}
if !compareNillableStrings(input.Name, v.ProductName) {
return nil, "PENDING", nil
}
if !compareNillableUints(input.RateLimit, v.RateLimit) {
return nil, "PENDING", nil
}
if !compareNillableUints(input.VlanA, v.AEnd.Vlan) {
return nil, "PENDING", nil
}
pc := input.PartnerConfig.(*api.PartnerConfigAWS)
if !compareNillableStrings(pc.AmazonIPAddress, v.Resources.AwsVirtualInterface.AmazonIpAddress) {
return nil, "PENDING", nil
}
if !compareNillableStrings(pc.AWSAccountID, v.Resources.AwsVirtualInterface.OwnerAccount) {
return nil, "PENDING", nil
}
if !compareNillableStrings(pc.AWSConnectionName, v.Resources.AwsVirtualInterface.Name) {
return nil, "PENDING", nil
}
if !compareNillableStrings(pc.BGPAuthKey, v.Resources.AwsVirtualInterface.AuthKey) {
return nil, "PENDING", nil
}
if v.ProductName != *input.Name {
if !compareNillableUints(pc.CustomerASN, v.Resources.AwsVirtualInterface.Asn) {
return nil, "PENDING", nil
}
if v.RateLimit != *input.RateLimit {
if !compareNillableStrings(pc.CustomerIPAddress, v.Resources.AwsVirtualInterface.CustomerIpAddress) {
return nil, "PENDING", nil
}
if v.AEnd.Vlan != *input.VlanA {
if !compareNillableStrings(pc.Type, strings.ToLower(v.Resources.AwsVirtualInterface.Type)) {
return nil, "PENDING", nil
}
return v, "UPDATED", nil
Expand Down
48 changes: 38 additions & 10 deletions megaport/resource_megaport_aws_vxc_test.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
package megaport

import (
"math/rand"
"net"
"strconv"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/utilitywarehouse/terraform-provider-megaport/megaport/api"
)

func TestAccMegaportAwsVxc_basic(t *testing.T) {
var vxcBefore api.ProductAssociatedVxc
var (
vxc, vxcUpdated api.ProductAssociatedVxc
port api.Product
)
rName := "t" + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
rId := acctest.RandStringFromCharSet(12, "012346789")
rAsn := uint64(acctest.RandIntRange(1, 65535))

rand.Seed(time.Now().UnixNano())
n := &net.IPNet{
IP: net.IPv4(169, 254, byte(rand.Intn(255)), byte(rand.Intn(63)*4+1)),
Mask: net.CIDRMask(30, 32),
}
ipA := n.String()
n.IP[len(n.IP)-1]++
ipB := n.String()
configValues := map[string]interface{}{
"uid": rName,
"location": "Equinix LD5",
"aws_account_id": rId,
"customer_asn": rAsn,
"uid": rName,
"location": "Equinix LD5",
"aws_account_id": rId,
"customer_asn": rAsn,
"aws_ip_address": ipA,
"customer_ip_address": ipB,
}
cfg, err := testAccGetConfig("megaport_aws_vxc_basic", configValues, 0)
if err != nil {
Expand All @@ -38,35 +53,48 @@ func TestAccMegaportAwsVxc_basic(t *testing.T) {
{
Config: cfg,
Check: resource.ComposeTestCheckFunc(
testAccCheckResourceExists("megaport_port.foo", &vxcBefore),
testAccCheckResourceExists("megaport_aws_vxc.foo", &vxcBefore),
testAccCheckResourceExists("megaport_port.foo", &port),
testAccCheckResourceExists("megaport_aws_vxc.foo", &vxc),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "name", "terraform_acctest_"+rName),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "rate_limit", "100"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "invoice_reference", ""),
resource.TestCheckResourceAttrPair("megaport_aws_vxc.foo", "a_end.0.product_uid", "megaport_port.foo", "id"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "a_end.0.vlan", "567"),
resource.TestCheckResourceAttrPair("megaport_aws_vxc.foo", "b_end.0.product_uid", "data.megaport_partner_port.aws", "id"),
resource.TestCheckResourceAttrSet("megaport_aws_vxc.foo", "b_end.0.connected_product_uid"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.aws_account_id", rId),
resource.TestCheckResourceAttrSet("megaport_aws_vxc.foo", "b_end.0.aws_connection_name"),
resource.TestCheckResourceAttrSet("megaport_aws_vxc.foo", "b_end.0.aws_ip_address"),
resource.TestCheckResourceAttrSet("megaport_aws_vxc.foo", "b_end.0.bgp_auth_key"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.customer_asn", strconv.Itoa(int(rAsn))),
resource.TestCheckResourceAttrSet("megaport_aws_vxc.foo", "b_end.0.customer_ip_address"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.type", "private"),
),
},
{
Config: cfgUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckResourceExists("megaport_port.foo", &vxcBefore),
testAccCheckResourceExists("megaport_aws_vxc.foo", &vxcBefore),
testAccCheckResourceExists("megaport_port.foo", &port),
testAccCheckResourceExists("megaport_aws_vxc.foo", &vxcUpdated),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "name", "terraform_acctest_"+rName),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "rate_limit", "1000"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "invoice_reference", rName),
resource.TestCheckResourceAttrPair("megaport_aws_vxc.foo", "a_end.0.product_uid", "megaport_port.foo", "id"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "a_end.0.vlan", "568"),
resource.TestCheckResourceAttrPair("megaport_aws_vxc.foo", "b_end.0.product_uid", "data.megaport_partner_port.aws", "id"),
resource.TestCheckResourceAttrSet("megaport_aws_vxc.foo", "b_end.0.connected_product_uid"),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.aws_account_id", rId),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.aws_connection_name", rName),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.aws_ip_address", ipA),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.bgp_auth_key", rName),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.customer_asn", strconv.Itoa(int(rAsn))),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.customer_ip_address", ipB),
resource.TestCheckResourceAttr("megaport_aws_vxc.foo", "b_end.0.type", "private"),
),
},
},
})
if vxc.ProductUid != vxcUpdated.ProductUid {
t.Errorf("TestAccMegaportAwsVxc_basic: expected the VXC to be updated but the resource ids differ")
}
}

0 comments on commit 719e4bc

Please sign in to comment.