github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/cloud-subnet.go (about) 1 package powervs 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 gohttp "net/http" 8 "strings" 9 "time" 10 11 "github.com/IBM/go-sdk-core/v5/core" 12 "github.com/IBM/vpc-go-sdk/vpcv1" 13 "k8s.io/apimachinery/pkg/util/wait" 14 ) 15 16 const cloudSubnetTypeName = "cloudSubnet" 17 18 // listCloudSubnets lists subnets in the VPC cloud. 19 func (o *ClusterUninstaller) listCloudSubnets() (cloudResources, error) { 20 o.Logger.Debugf("Listing virtual Cloud Subnets") 21 22 ctx, cancel := o.contextWithTimeout() 23 defer cancel() 24 25 select { 26 case <-ctx.Done(): 27 o.Logger.Debugf("listCloudSubnets: case <-ctx.Done()") 28 return nil, o.Context.Err() // we're cancelled, abort 29 default: 30 } 31 32 options := o.vpcSvc.NewListSubnetsOptions() 33 subnets, detailedResponse, err := o.vpcSvc.ListSubnets(options) 34 35 if err != nil { 36 return nil, fmt.Errorf("failed to list subnets and the response is: %s: %w", detailedResponse, err) 37 } 38 39 var foundOne = false 40 41 result := []cloudResource{} 42 for _, subnet := range subnets.Subnets { 43 if strings.Contains(*subnet.Name, o.InfraID) { 44 foundOne = true 45 o.Logger.Debugf("listCloudSubnets: FOUND: %s, %s", *subnet.ID, *subnet.Name) 46 result = append(result, cloudResource{ 47 key: *subnet.ID, 48 name: *subnet.Name, 49 status: "", 50 typeName: cloudSubnetTypeName, 51 id: *subnet.ID, 52 }) 53 } 54 } 55 if !foundOne { 56 o.Logger.Debugf("listCloudSubnets: NO matching subnet against: %s", o.InfraID) 57 for _, subnet := range subnets.Subnets { 58 o.Logger.Debugf("listCloudSubnets: subnet: %s", *subnet.Name) 59 } 60 } 61 62 return cloudResources{}.insert(result...), nil 63 } 64 65 func (o *ClusterUninstaller) deleteCloudSubnet(item cloudResource) error { 66 var getOptions *vpcv1.GetSubnetOptions 67 var response *core.DetailedResponse 68 var err error 69 70 ctx, cancel := o.contextWithTimeout() 71 defer cancel() 72 73 select { 74 case <-ctx.Done(): 75 o.Logger.Debugf("deleteCloudSubnet: case <-ctx.Done()") 76 return o.Context.Err() // we're cancelled, abort 77 default: 78 } 79 80 getOptions = o.vpcSvc.NewGetSubnetOptions(item.id) 81 _, response, err = o.vpcSvc.GetSubnet(getOptions) 82 83 if err != nil && response != nil && response.StatusCode == gohttp.StatusNotFound { 84 // The resource is gone 85 o.deletePendingItems(item.typeName, []cloudResource{item}) 86 o.Logger.Infof("Deleted Subnet %q", item.name) 87 return nil 88 } 89 if err != nil && response != nil && response.StatusCode == gohttp.StatusInternalServerError { 90 o.Logger.Infof("deleteCloudSubnet: internal server error") 91 return nil 92 } 93 94 deleteOptions := o.vpcSvc.NewDeleteSubnetOptions(item.id) 95 _, err = o.vpcSvc.DeleteSubnetWithContext(ctx, deleteOptions) 96 if err != nil { 97 return fmt.Errorf("failed to delete subnet %s: %w", item.name, err) 98 } 99 100 o.Logger.Infof("Deleted Subnet %q", item.name) 101 o.deletePendingItems(item.typeName, []cloudResource{item}) 102 103 return nil 104 } 105 106 // destroyCloudSubnets removes all subnet resources that have a name prefixed 107 // with the cluster's infra ID. 108 func (o *ClusterUninstaller) destroyCloudSubnets() error { 109 firstPassList, err := o.listCloudSubnets() 110 if err != nil { 111 return err 112 } 113 114 if len(firstPassList.list()) == 0 { 115 return nil 116 } 117 118 items := o.insertPendingItems(cloudSubnetTypeName, firstPassList.list()) 119 120 ctx, cancel := o.contextWithTimeout() 121 defer cancel() 122 123 for _, item := range items { 124 select { 125 case <-ctx.Done(): 126 o.Logger.Debugf("destroyCloudSubnets: case <-ctx.Done()") 127 return o.Context.Err() // we're cancelled, abort 128 default: 129 } 130 131 backoff := wait.Backoff{ 132 Duration: 15 * time.Second, 133 Factor: 1.1, 134 Cap: leftInContext(ctx), 135 Steps: math.MaxInt32} 136 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 137 err2 := o.deleteCloudSubnet(item) 138 if err2 == nil { 139 return true, err2 140 } 141 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 142 return false, err2 143 }) 144 if err != nil { 145 o.Logger.Fatal("destroyCloudSubnets: ExponentialBackoffWithContext (destroy) returns ", err) 146 } 147 } 148 149 if items = o.getPendingItems(cloudSubnetTypeName); len(items) > 0 { 150 for _, item := range items { 151 o.Logger.Debugf("destroyCloudSubnets: found %s in pending items", item.name) 152 } 153 return fmt.Errorf("destroyCloudSubnets: %d undeleted items pending", len(items)) 154 } 155 156 backoff := wait.Backoff{ 157 Duration: 15 * time.Second, 158 Factor: 1.1, 159 Cap: leftInContext(ctx), 160 Steps: math.MaxInt32} 161 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 162 secondPassList, err2 := o.listCloudSubnets() 163 if err2 != nil { 164 return false, err2 165 } 166 if len(secondPassList) == 0 { 167 // We finally don't see any remaining instances! 168 return true, nil 169 } 170 for _, item := range secondPassList { 171 o.Logger.Debugf("destroyCloudSubnets: found %s in second pass", item.name) 172 } 173 return false, nil 174 }) 175 if err != nil { 176 o.Logger.Fatal("destroyCloudSubnets: ExponentialBackoffWithContext (list) returns ", err) 177 } 178 179 return nil 180 }