From 61a70ab9adb1d6839fce23f2c31a4c0cdca74d10 Mon Sep 17 00:00:00 2001 From: morozovalekseywot Date: Wed, 4 Dec 2024 21:20:10 +0300 Subject: [PATCH] add waiting for port of share --- vkcs/networking/subnet.go | 41 +------- vkcs/sharedfilesystem/resource_share.go | 128 ++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 46 deletions(-) diff --git a/vkcs/networking/subnet.go b/vkcs/networking/subnet.go index a1d84944..48bfbb2e 100644 --- a/vkcs/networking/subnet.go +++ b/vkcs/networking/subnet.go @@ -94,12 +94,8 @@ func networkingSubnetStateRefreshFuncDelete(networkingClient *gophercloud.Servic // Subnet is still in use - we can retry. if errutil.Is(err, 409) { - log.Printf("[DEBUG] Failed to delete vkcs_networking_subnet %s, subnet is still in use. Trying to delete active ports", subnetID) + log.Printf("[DEBUG] Failed to delete vkcs_networking_subnet %s, subnet is still in use.", subnetID) *deleteErrDetails = err - if deletePortErr := deleteSubnetPorts(networkingClient, subnet); deletePortErr != nil { - log.Printf("[DEBUG] Failed to delete ports of subnet %s: %s", subnetID, err) - } - return subnet, "ACTIVE", nil } @@ -112,41 +108,6 @@ func networkingSubnetStateRefreshFuncDelete(networkingClient *gophercloud.Servic } } -func deleteSubnetPorts(networkingClient *gophercloud.ServiceClient, subnet *subnets.Subnet) error { - var listOpts ports.ListOptsBuilder = ports.ListOpts{ - ProjectID: subnet.ProjectID, - NetworkID: subnet.NetworkID, - Status: "ACTIVE", - FixedIPs: []ports.FixedIPOpts{ - { - SubnetID: subnet.ID, - }, - }, - } - - allPages, err := ports.List(networkingClient, listOpts).AllPages() - if err != nil { - return err - } - - var allPorts []struct { - ID string `json:"id"` - } - err = ports.ExtractPortsInto(allPages, &allPorts) - if err != nil { - return err - } - - for _, port := range allPorts { - err = ports.Delete(networkingClient, port.ID).ExtractErr() - if err != nil && !errutil.IsNotFound(err) { - log.Printf("[DEBUG] Failed to delete port %s of subnet %s: %s", port.ID, subnet.ID, err) - } - } - - return nil -} - // networkingSubnetGetRawAllocationPoolsValueToExpand selects the resource argument to populate // the allocations pool value. func networkingSubnetGetRawAllocationPoolsValueToExpand(d *schema.ResourceData) []interface{} { diff --git a/vkcs/sharedfilesystem/resource_share.go b/vkcs/sharedfilesystem/resource_share.go index 9c5e4916..73638289 100644 --- a/vkcs/sharedfilesystem/resource_share.go +++ b/vkcs/sharedfilesystem/resource_share.go @@ -4,13 +4,17 @@ import ( "context" "fmt" "log" + "strings" "time" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/vk-cs/terraform-provider-vkcs/vkcs/internal/clients" + inetworking "github.com/vk-cs/terraform-provider-vkcs/vkcs/internal/services/networking" + iports "github.com/vk-cs/terraform-provider-vkcs/vkcs/internal/services/networking/v2/ports" "github.com/vk-cs/terraform-provider-vkcs/vkcs/internal/util" "github.com/gophercloud/gophercloud" @@ -24,9 +28,9 @@ import ( const ( // Major share functionality appeared in 2.14. minManilaShareMicroversion = "2.14" - shareOperationCreateTimeout = 60 - shareOperationUpdateTimeout = 40 - shareOperationDeleteTimeout = 20 + shareOperationCreateTimeout = 120 + shareOperationUpdateTimeout = 60 + shareOperationDeleteTimeout = 120 ) func ResourceSharedFilesystemShare() *schema.Resource { @@ -195,6 +199,7 @@ func resourceSharedFilesystemShareCreate(ctx context.Context, d *schema.Resource if err != nil { return diag.FromErr(err) } + time.Sleep(2 * time.Minute) return resourceSharedFilesystemShareRead(ctx, d, meta) } @@ -350,6 +355,27 @@ func resourceSharedFilesystemShareDelete(ctx context.Context, d *schema.Resource if err != nil { return diag.Errorf("Error creating VKCS sharedfilesystem client: %s", err) } + sfsClient.Microversion = minManilaShareMicroversion + + networkingClient, err := config.NetworkingV2Client(util.GetRegion(d, config), inetworking.SearchInAllSDNs) + if err != nil { + return diag.Errorf("Error creating VKCS networking client: %s", err) + } + + share, getErr := ishares.Get(sfsClient, d.Id()).Extract() + if getErr != nil { + err = util.CheckDeleted(d, err, "Unable to delete share") + if err != nil { + return diag.FromErr(err) + } + + return nil + } + + portID, err := getSubnetPort(networkingClient, sfsClient, share) + if err != nil { + return diag.Errorf("Unable to delete %s share: %s", d.Id(), err) + } timeout := d.Timeout(schema.TimeoutDelete) @@ -365,13 +391,15 @@ func resourceSharedFilesystemShareDelete(ctx context.Context, d *schema.Resource if err != nil { e := util.CheckDeleted(d, err, "") if e == nil { - return nil + return waitForPortDeleting(ctx, networkingClient, portID, timeout) } + detailedErr := sfserrors.ErrorDetails{} e = sfserrors.ExtractErrorInto(err, &detailedErr) if e != nil { return diag.Errorf("Unable to delete %s share: %s: %s", d.Id(), err, e) } + for k, msg := range detailedErr { return diag.Errorf("Unable to delete %s share: %s (%d): %s", d.Id(), k, msg.Code, msg.Message) } @@ -384,10 +412,9 @@ func resourceSharedFilesystemShareDelete(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - return nil + return waitForPortDeleting(ctx, networkingClient, portID, timeout) } -// Full list of the share statuses: https://developer.vkcs.org/api-ref/shared-file-system/#shares func waitForSFShare(ctx context.Context, sfsClient *gophercloud.ServiceClient, id string, target string, pending []string, timeout time.Duration) error { log.Printf("[DEBUG] Waiting for share %s to become %s.", id, target) @@ -410,11 +437,13 @@ func waitForSFShare(ctx context.Context, sfsClient *gophercloud.ServiceClient, i return fmt.Errorf("error: share %s not found: %s", id, err) } } + errorMessage := fmt.Sprintf("error waiting for share %s to become %s", id, target) msg := resourceSFSShareManilaMessage(sfsClient, id) if msg == nil { return fmt.Errorf("%s: %s", errorMessage, err) } + return fmt.Errorf("%s: %s: the latest manila message (%s): %s", errorMessage, err, msg.CreatedAt, msg.UserMessage) } @@ -460,3 +489,90 @@ func resourceSFSShareManilaMessage(sfsClient *gophercloud.ServiceClient, id stri return &allMessages[0] } + +func getSubnetPort(networkingClient, sfsClient *gophercloud.ServiceClient, share *shares.Share) (string, error) { + exportLocationPath, err := getShareExportLocationPath(sfsClient, share.ID) + if err != nil { + return "", fmt.Errorf("error retrieving share export location path: %s", err) + } + + portIP, _, found := strings.Cut(exportLocationPath, ":/shares") + if !found { + return "", nil + } + + listOpts := ports.ListOpts{ + FixedIPs: []ports.FixedIPOpts{ + { + IPAddress: portIP, + }, + }, + } + allPages, err := ports.List(networkingClient, listOpts).AllPages() + if err != nil { + return "", fmt.Errorf("error listing ports: %s", err) + } + + var sharePorts []struct { + ID string `json:"id"` + } + err = ports.ExtractPortsInto(allPages, &sharePorts) + if err != nil { + return "", fmt.Errorf("error reading VKCS Networking API response: %s", err) + } + + if len(sharePorts) == 0 { + return "", nil + } + + return sharePorts[0].ID, nil +} + +func waitForPortDeleting(ctx context.Context, networkingClient *gophercloud.ServiceClient, portID string, timeout time.Duration) diag.Diagnostics { + if portID == "" { + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to delete share's port in subnet", + Detail: "Couldn't get port ip address from share", + }, + } + } + + log.Printf("[DEBUG] Waiting for share's port %s to become deleted.", portID) + stateConf := &retry.StateChangeConf{ + Target: []string{"DELETED"}, + Refresh: resourceNetworkingPortStateRefreshFunc(networkingClient, portID), + Timeout: timeout, + Delay: 5 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: fmt.Sprintf("Failed to delete share's port %s in subnet", portID), + Detail: err.Error(), + }, + } + } + + return nil +} + +func resourceNetworkingPortStateRefreshFunc(client *gophercloud.ServiceClient, portID string) retry.StateRefreshFunc { + return func() (any, string, error) { + port, err := iports.Get(client, portID).Extract() + if err != nil { + if errutil.IsNotFound(err) { + return port, "DELETED", nil + } + + return port, "", err + } + + return port, port.Status, nil + } +}