Skip to content

Commit

Permalink
add waiting for port of share
Browse files Browse the repository at this point in the history
  • Loading branch information
morozovalekseywot committed Dec 4, 2024
1 parent 39746e2 commit 61a70ab
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 46 deletions.
41 changes: 1 addition & 40 deletions vkcs/networking/subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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{} {
Expand Down
128 changes: 122 additions & 6 deletions vkcs/sharedfilesystem/resource_share.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)

Expand All @@ -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)
}
Expand All @@ -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)

Expand All @@ -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)
}

Expand Down Expand Up @@ -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
}
}

0 comments on commit 61a70ab

Please sign in to comment.