From 58426426430b7ba2955a72014a0074791d1f7a4e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 16 May 2017 02:36:50 -0600 Subject: [PATCH] provider/openstack: Handle Deleted Resources in Floating IP Association (#14533) This commit modifies the openstack_compute_floatingip_associate_v2 resource to handle cases where the floating IP or instance were deleted outside of Terraform. --- ...enstack_compute_floatingip_associate_v2.go | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_floatingip_associate_v2.go b/builtin/providers/openstack/resource_openstack_compute_floatingip_associate_v2.go index c2e1d6888..963e191d6 100644 --- a/builtin/providers/openstack/resource_openstack_compute_floatingip_associate_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_floatingip_associate_v2.go @@ -5,7 +5,10 @@ import ( "log" "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + nfloatingips "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/hashicorp/terraform/helper/schema" ) @@ -79,12 +82,67 @@ func resourceComputeFloatingIPAssociateV2Create(d *schema.ResourceData, meta int } func resourceComputeFloatingIPAssociateV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + computeClient, err := config.computeV2Client(GetRegion(d)) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + // Obtain relevant info from parsing the ID floatingIP, instanceId, fixedIP, err := parseComputeFloatingIPAssociateId(d.Id()) if err != nil { return err } + // Now check and see whether the floating IP still exists. + // First try to do this by querying the Network API. + networkEnabled := true + networkClient, err := config.networkingV2Client(GetRegion(d)) + if err != nil { + networkEnabled = false + } + + var exists bool + if networkEnabled { + log.Printf("[DEBUG] Checking for Floating IP existence via Network API") + exists, err = resourceComputeFloatingIPAssociateV2NetworkExists(networkClient, floatingIP) + } else { + log.Printf("[DEBUG] Checking for Floating IP existence via Compute API") + exists, err = resourceComputeFloatingIPAssociateV2ComputeExists(computeClient, floatingIP) + } + + if err != nil { + return err + } + + if !exists { + d.SetId("") + } + + // Next, see if the instance still exists + instance, err := servers.Get(computeClient, instanceId).Extract() + if err != nil { + if CheckDeleted(d, err, "instance") == nil { + return nil + } + } + + // Finally, check and see if the floating ip is still associated with the instance. + var associated bool + for _, networkAddresses := range instance.Addresses { + for _, element := range networkAddresses.([]interface{}) { + address := element.(map[string]interface{}) + if address["OS-EXT-IPS:type"] == "floating" && address["addr"] == floatingIP { + associated = true + } + } + } + + if !associated { + d.SetId("") + } + + // Set the attributes pulled from the composed resource ID d.Set("floating_ip", floatingIP) d.Set("instance_id", instanceId) d.Set("fixed_ip", fixedIP) @@ -128,3 +186,49 @@ func parseComputeFloatingIPAssociateId(id string) (string, string, string, error return floatingIP, instanceId, fixedIP, nil } + +func resourceComputeFloatingIPAssociateV2NetworkExists(networkClient *gophercloud.ServiceClient, floatingIP string) (bool, error) { + listOpts := nfloatingips.ListOpts{ + FloatingIP: floatingIP, + } + allPages, err := nfloatingips.List(networkClient, listOpts).AllPages() + if err != nil { + return false, err + } + + allFips, err := nfloatingips.ExtractFloatingIPs(allPages) + if err != nil { + return false, err + } + + if len(allFips) > 1 { + return false, fmt.Errorf("There was a problem retrieving the floating IP") + } + + if len(allFips) == 0 { + return false, nil + } + + return true, nil +} + +func resourceComputeFloatingIPAssociateV2ComputeExists(computeClient *gophercloud.ServiceClient, floatingIP string) (bool, error) { + // If the Network API isn't available, fall back to the deprecated Compute API. + allPages, err := floatingips.List(computeClient).AllPages() + if err != nil { + return false, err + } + + allFips, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + return false, err + } + + for _, f := range allFips { + if f.IP == floatingIP { + return true, nil + } + } + + return false, nil +}