github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/loadbalancer.go (about) 1 package powervs 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "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 loadBalancerTypeName = "load balancer" 17 18 // listLoadBalancers lists load balancers in the vpc. 19 func (o *ClusterUninstaller) listLoadBalancers() (cloudResources, error) { 20 o.Logger.Debugf("Listing load balancers") 21 22 ctx, cancel := o.contextWithTimeout() 23 defer cancel() 24 25 select { 26 case <-ctx.Done(): 27 o.Logger.Debugf("listLoadBalancers: case <-ctx.Done()") 28 return nil, o.Context.Err() // we're cancelled, abort 29 default: 30 } 31 32 options := o.vpcSvc.NewListLoadBalancersOptions() 33 34 resources, _, err := o.vpcSvc.ListLoadBalancersWithContext(ctx, options) 35 if err != nil { 36 return nil, fmt.Errorf("failed to list load balancers: %w", err) 37 } 38 39 var foundOne = false 40 41 result := []cloudResource{} 42 for _, loadbalancer := range resources.LoadBalancers { 43 if strings.Contains(*loadbalancer.Name, o.InfraID) { 44 foundOne = true 45 o.Logger.Debugf("listLoadBalancers: FOUND: %s, %s, %s", *loadbalancer.ID, *loadbalancer.Name, *loadbalancer.ProvisioningStatus) 46 result = append(result, cloudResource{ 47 key: *loadbalancer.ID, 48 name: *loadbalancer.Name, 49 status: *loadbalancer.ProvisioningStatus, 50 typeName: loadBalancerTypeName, 51 id: *loadbalancer.ID, 52 }) 53 } 54 } 55 if !foundOne { 56 o.Logger.Debugf("listLoadBalancers: NO matching loadbalancers against: %s", o.InfraID) 57 for _, loadbalancer := range resources.LoadBalancers { 58 o.Logger.Debugf("listLoadBalancers: loadbalancer: %s", *loadbalancer.Name) 59 } 60 } 61 62 return cloudResources{}.insert(result...), nil 63 } 64 65 func (o *ClusterUninstaller) deleteLoadBalancer(item cloudResource) error { 66 var getOptions *vpcv1.GetLoadBalancerOptions 67 var lb *vpcv1.LoadBalancer 68 var response *core.DetailedResponse 69 var err error 70 71 ctx, cancel := o.contextWithTimeout() 72 defer cancel() 73 74 select { 75 case <-ctx.Done(): 76 o.Logger.Debugf("deleteLoadBalancer: case <-ctx.Done()") 77 return o.Context.Err() // we're cancelled, abort 78 default: 79 } 80 81 getOptions = o.vpcSvc.NewGetLoadBalancerOptions(item.id) 82 lb, response, err = o.vpcSvc.GetLoadBalancer(getOptions) 83 84 if err == nil && response.StatusCode == http.StatusNoContent { 85 return nil 86 } 87 if err != nil && response != nil && response.StatusCode == http.StatusNotFound { 88 // The resource is gone. 89 o.deletePendingItems(item.typeName, []cloudResource{item}) 90 o.Logger.Infof("Deleted Load Balancer %q", item.name) 91 return nil 92 } 93 if err != nil && response != nil && response.StatusCode == http.StatusInternalServerError { 94 o.Logger.Infof("deleteLoadBalancer: internal server error") 95 return nil 96 } 97 if lb == nil { 98 o.Logger.Debugf("deleteLoadBalancer: lb = %v", lb) 99 o.Logger.Debugf("deleteLoadBalancer: response = %v", response) 100 o.Logger.Debugf("deleteLoadBalancer: err = %v", err) 101 o.Logger.Debugf("Rate and unhandled code, please investigate further") 102 return nil 103 } 104 105 if *lb.ProvisioningStatus == vpcv1.LoadBalancerProvisioningStatusDeletePendingConst { 106 o.Logger.Debugf("Waiting for load balancer %q to delete", item.name) 107 return nil 108 } 109 110 deleteOptions := o.vpcSvc.NewDeleteLoadBalancerOptions(item.id) 111 112 _, err = o.vpcSvc.DeleteLoadBalancerWithContext(ctx, deleteOptions) 113 if err != nil { 114 return fmt.Errorf("failed to delete load balancer %s: %w", item.name, err) 115 } 116 117 o.Logger.Infof("Deleted Load Balancer %q", item.name) 118 o.deletePendingItems(item.typeName, []cloudResource{item}) 119 120 return nil 121 } 122 123 // destroyLoadBalancers removes all load balancer resources that have a name prefixed 124 // with the cluster's infra ID. 125 func (o *ClusterUninstaller) destroyLoadBalancers() error { 126 firstPassList, err := o.listLoadBalancers() 127 if err != nil { 128 return err 129 } 130 131 if len(firstPassList.list()) == 0 { 132 return nil 133 } 134 135 items := o.insertPendingItems(loadBalancerTypeName, firstPassList.list()) 136 137 ctx, cancel := o.contextWithTimeout() 138 defer cancel() 139 140 for _, item := range items { 141 select { 142 case <-ctx.Done(): 143 o.Logger.Debugf("destroyLoadBalancers: case <-ctx.Done()") 144 return o.Context.Err() // we're cancelled, abort 145 default: 146 } 147 148 backoff := wait.Backoff{ 149 Duration: 15 * time.Second, 150 Factor: 1.1, 151 Cap: leftInContext(ctx), 152 Steps: math.MaxInt32} 153 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 154 err2 := o.deleteLoadBalancer(item) 155 if err2 == nil { 156 return true, err2 157 } 158 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 159 return false, err2 160 }) 161 if err != nil { 162 o.Logger.Fatal("destroyLoadBalancers: ExponentialBackoffWithContext (destroy) returns ", err) 163 } 164 } 165 166 if items = o.getPendingItems(loadBalancerTypeName); len(items) > 0 { 167 for _, item := range items { 168 o.Logger.Debugf("destroyLoadBalancers: found %s in pending items", item.name) 169 } 170 return fmt.Errorf("destroyLoadBalancers: %d undeleted items pending", len(items)) 171 } 172 173 backoff := wait.Backoff{ 174 Duration: 15 * time.Second, 175 Factor: 1.1, 176 Cap: leftInContext(ctx), 177 Steps: math.MaxInt32} 178 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 179 secondPassList, err2 := o.listLoadBalancers() 180 if err2 != nil { 181 return false, err2 182 } 183 if len(secondPassList) == 0 { 184 // We finally don't see any remaining instances! 185 return true, nil 186 } 187 for _, item := range secondPassList { 188 o.Logger.Debugf("destroyLoadBalancers: found %s in second pass", item.name) 189 } 190 return false, nil 191 }) 192 if err != nil { 193 o.Logger.Fatal("destroyLoadBalancers: ExponentialBackoffWithContext (list) returns ", err) 194 } 195 196 return nil 197 }