Openstack port update fixes (#13604)

* provider/openstack: Handle 409 Errors Upon Security Group Deletion

If a security group is currently in use, it will throw a 409 error.
This commit catches the 409, allowing other resources to finish
deleting.

* Update openstack_networking_port_v2 resource to pass empty arrays for AllowedAddressPairs and SecurityGroups if not specified. Fixes #13531

* provider/openstack: Port Update comment
This commit is contained in:
Joe Topjian 2017-04-13 05:37:15 -06:00 committed by Paul Stack
parent 3080686a68
commit 6efd0640ec
3 changed files with 164 additions and 13 deletions

View File

@ -236,7 +236,14 @@ func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error creating OpenStack networking client: %s", err) return fmt.Errorf("Error creating OpenStack networking client: %s", err)
} }
var updateOpts ports.UpdateOpts // security_group_ids and allowed_address_pairs are able to send empty arrays
// to denote the removal of each. But their default zero-value is translated
// to "null", which has been reported to cause problems in vendor-modified
// OpenStack clouds. Therefore, we must set them in each request update.
updateOpts := ports.UpdateOpts{
AllowedAddressPairs: resourceAllowedAddressPairsV2(d),
SecurityGroups: resourcePortSecurityGroupsV2(d),
}
if d.HasChange("name") { if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string) updateOpts.Name = d.Get("name").(string)
@ -250,10 +257,6 @@ func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) er
updateOpts.DeviceOwner = d.Get("device_owner").(string) updateOpts.DeviceOwner = d.Get("device_owner").(string)
} }
if d.HasChange("security_group_ids") {
updateOpts.SecurityGroups = resourcePortSecurityGroupsV2(d)
}
if d.HasChange("device_id") { if d.HasChange("device_id") {
updateOpts.DeviceID = d.Get("device_id").(string) updateOpts.DeviceID = d.Get("device_id").(string)
} }
@ -262,10 +265,6 @@ func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) er
updateOpts.FixedIPs = resourcePortFixedIpsV2(d) updateOpts.FixedIPs = resourcePortFixedIpsV2(d)
} }
if d.HasChange("allowed_address_pairs") {
updateOpts.AllowedAddressPairs = resourceAllowedAddressPairsV2(d)
}
log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts) log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)
_, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract() _, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract()
@ -332,10 +331,6 @@ func resourceAllowedAddressPairsV2(d *schema.ResourceData) []ports.AddressPair {
// ports.AddressPair // ports.AddressPair
rawPairs := d.Get("allowed_address_pairs").(*schema.Set).List() rawPairs := d.Get("allowed_address_pairs").(*schema.Set).List()
if len(rawPairs) == 0 {
return nil
}
pairs := make([]ports.AddressPair, len(rawPairs)) pairs := make([]ports.AddressPair, len(rawPairs))
for i, raw := range rawPairs { for i, raw := range rawPairs {
rawMap := raw.(map[string]interface{}) rawMap := raw.(map[string]interface{})

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
@ -168,6 +169,54 @@ func TestAccNetworkingV2Port_fixedIPs(t *testing.T) {
}) })
} }
func TestAccNetworkingV2Port_updateSecurityGroups(t *testing.T) {
var network networks.Network
var port ports.Port
var security_group groups.SecGroup
var subnet subnets.Subnet
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2PortDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2Port_updateSecurityGroups_1,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port),
testAccCheckNetworkingV2SecGroupExists(
"openstack_networking_secgroup_v2.secgroup_1", &security_group),
testAccCheckNetworkingV2PortCountSecurityGroups(&port, 1),
),
},
resource.TestStep{
Config: testAccNetworkingV2Port_updateSecurityGroups_2,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port),
testAccCheckNetworkingV2SecGroupExists(
"openstack_networking_secgroup_v2.secgroup_1", &security_group),
testAccCheckNetworkingV2PortCountSecurityGroups(&port, 1),
),
},
resource.TestStep{
Config: testAccNetworkingV2Port_updateSecurityGroups_3,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port),
testAccCheckNetworkingV2SecGroupExists(
"openstack_networking_secgroup_v2.secgroup_1", &security_group),
testAccCheckNetworkingV2PortCountSecurityGroups(&port, 0),
),
},
},
})
}
func testAccCheckNetworkingV2PortDestroy(s *terraform.State) error { func testAccCheckNetworkingV2PortDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME) networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
@ -231,6 +280,16 @@ func testAccCheckNetworkingV2PortCountFixedIPs(port *ports.Port, expected int) r
} }
} }
func testAccCheckNetworkingV2PortCountSecurityGroups(port *ports.Port, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if len(port.SecurityGroups) != expected {
return fmt.Errorf("Expected %d Security Groups, got %d", expected, len(port.SecurityGroups))
}
return nil
}
}
const testAccNetworkingV2Port_basic = ` const testAccNetworkingV2Port_basic = `
resource "openstack_networking_network_v2" "network_1" { resource "openstack_networking_network_v2" "network_1" {
name = "network_1" name = "network_1"
@ -472,3 +531,95 @@ resource "openstack_networking_port_v2" "port_1" {
} }
} }
` `
const testAccNetworkingV2Port_updateSecurityGroups_1 = `
resource "openstack_networking_network_v2" "network_1" {
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
name = "subnet_1"
cidr = "192.168.199.0/24"
ip_version = 4
network_id = "${openstack_networking_network_v2.network_1.id}"
}
resource "openstack_networking_secgroup_v2" "secgroup_1" {
name = "security_group"
description = "terraform security group acceptance test"
}
resource "openstack_networking_port_v2" "port_1" {
name = "port_1"
admin_state_up = "true"
network_id = "${openstack_networking_network_v2.network_1.id}"
fixed_ip {
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
ip_address = "192.168.199.23"
}
}
`
const testAccNetworkingV2Port_updateSecurityGroups_2 = `
resource "openstack_networking_network_v2" "network_1" {
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
name = "subnet_1"
cidr = "192.168.199.0/24"
ip_version = 4
network_id = "${openstack_networking_network_v2.network_1.id}"
}
resource "openstack_networking_secgroup_v2" "secgroup_1" {
name = "security_group"
description = "terraform security group acceptance test"
}
resource "openstack_networking_port_v2" "port_1" {
name = "port_1"
admin_state_up = "true"
network_id = "${openstack_networking_network_v2.network_1.id}"
security_group_ids = ["${openstack_networking_secgroup_v2.secgroup_1.id}"]
fixed_ip {
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
ip_address = "192.168.199.23"
}
}
`
const testAccNetworkingV2Port_updateSecurityGroups_3 = `
resource "openstack_networking_network_v2" "network_1" {
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
name = "subnet_1"
cidr = "192.168.199.0/24"
ip_version = 4
network_id = "${openstack_networking_network_v2.network_1.id}"
}
resource "openstack_networking_secgroup_v2" "secgroup_1" {
name = "security_group"
description = "terraform security group acceptance test"
}
resource "openstack_networking_port_v2" "port_1" {
name = "port_1"
admin_state_up = "true"
network_id = "${openstack_networking_network_v2.network_1.id}"
security_group_ids = []
fixed_ip {
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
ip_address = "192.168.199.23"
}
}
`

View File

@ -167,6 +167,11 @@ func waitForSecGroupDelete(networkingClient *gophercloud.ServiceClient, secGroup
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group %s", secGroupId) log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group %s", secGroupId)
return r, "DELETED", nil return r, "DELETED", nil
} }
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
if errCode.Actual == 409 {
return r, "ACTIVE", nil
}
}
return r, "ACTIVE", err return r, "ACTIVE", err
} }