From fab697b2e1cd5e62c7b28ba333fee1d08b255850 Mon Sep 17 00:00:00 2001 From: morozovalekseywot Date: Fri, 29 Nov 2024 13:50:24 +0300 Subject: [PATCH] adding waiting for a share's port to be deleted --- CHANGELOG.md | 1 + vkcs/sharedfilesystem/resource_share.go | 122 +++++++++++++++++++++++- 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b404cd39..bd1d04f1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ description: |- #### v0.8.5 (unreleased) - Fix an issue with marking subnet as ready while DHCP service is not configured yet +- Fix an issue with marking share as deleted while share's port in subnet is still active - Fix panic on empty taint to vkcs_kubernetes_node_group resource - Fix order-induced changes in the plan for "databases" of the vkcs_db_user resource - Fix panic on VKCS Kubernetes API error in vkcs_kubernetes_clustertemplates data source diff --git a/vkcs/sharedfilesystem/resource_share.go b/vkcs/sharedfilesystem/resource_share.go index 908b6c54..650860bd 100644 --- a/vkcs/sharedfilesystem/resource_share.go +++ b/vkcs/sharedfilesystem/resource_share.go @@ -4,13 +4,16 @@ 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" "github.com/vk-cs/terraform-provider-vkcs/vkcs/internal/util" "github.com/gophercloud/gophercloud" @@ -26,7 +29,7 @@ const ( minManilaShareMicroversion = "2.14" shareOperationCreateTimeout = 40 shareOperationUpdateTimeout = 40 - shareOperationDeleteTimeout = 20 + shareOperationDeleteTimeout = 30 ) func ResourceSharedFilesystemShare() *schema.Resource { @@ -195,6 +198,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 +354,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 +390,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 +411,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 +436,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 +488,89 @@ 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) + err := retry.RetryContext(ctx, timeout, func() *retry.RetryError { + listOpts := ports.ListOpts{ + ID: portID, + } + allPages, err := ports.List(networkingClient, listOpts).AllPages() + if err != nil { + return util.CheckForRetryableError(err) + } + + var sharePorts []struct { + ID string `json:"id"` + } + err = ports.ExtractPortsInto(allPages, &sharePorts) + if err != nil { + return util.CheckForRetryableError(err) + } + + if len(sharePorts) == 0 { + return nil + } + + return retry.RetryableError(fmt.Errorf("port %s in subnet is still active", portID)) + }) + 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 +}