github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/vpc.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 vpcTypeName = "vpc" 17 18 // listVPCs lists VPCs in the cloud. 19 func (o *ClusterUninstaller) listVPCs() (cloudResources, error) { 20 o.Logger.Debugf("Listing VPCs") 21 22 ctx, cancel := o.contextWithTimeout() 23 defer cancel() 24 25 select { 26 case <-ctx.Done(): 27 o.Logger.Debugf("listVPCs: case <-ctx.Done()") 28 return nil, o.Context.Err() // we're cancelled, abort 29 default: 30 } 31 32 options := o.vpcSvc.NewListVpcsOptions() 33 vpcs, _, err := o.vpcSvc.ListVpcs(options) 34 35 if err != nil { 36 return nil, fmt.Errorf("failed to list vps: %w", err) 37 } 38 39 var foundOne = false 40 41 result := []cloudResource{} 42 for _, vpc := range vpcs.Vpcs { 43 if strings.Contains(*vpc.Name, o.InfraID) { 44 foundOne = true 45 o.Logger.Debugf("listVPCs: FOUND: %s, %s", *vpc.ID, *vpc.Name) 46 result = append(result, cloudResource{ 47 key: *vpc.ID, 48 name: *vpc.Name, 49 status: "", 50 typeName: vpcTypeName, 51 id: *vpc.ID, 52 }) 53 } 54 } 55 if !foundOne { 56 o.Logger.Debugf("listVPCs: NO matching vpc against: %s", o.InfraID) 57 for _, vpc := range vpcs.Vpcs { 58 o.Logger.Debugf("listVPCs: vpc: %s", *vpc.Name) 59 } 60 } 61 62 return cloudResources{}.insert(result...), nil 63 } 64 65 func (o *ClusterUninstaller) deleteVPC(item cloudResource) error { 66 var getOptions *vpcv1.GetVPCOptions 67 var getResponse *core.DetailedResponse 68 var deleteResponse *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("deleteVPC: case <-ctx.Done()") 77 return o.Context.Err() // we're cancelled, abort 78 default: 79 } 80 81 getOptions = o.vpcSvc.NewGetVPCOptions(item.id) 82 _, getResponse, err = o.vpcSvc.GetVPC(getOptions) 83 84 o.Logger.Debugf("deleteVPC: getResponse = %v", getResponse) 85 o.Logger.Debugf("deleteVPC: err = %v", err) 86 87 // Sadly, there is no way to get the status of this VPC to check on the results of the 88 // delete call. 89 90 if err == nil && getResponse.StatusCode == gohttp.StatusNoContent { 91 return nil 92 } 93 if err != nil && getResponse != nil && getResponse.StatusCode == gohttp.StatusNotFound { 94 // The resource is gone 95 o.deletePendingItems(item.typeName, []cloudResource{item}) 96 o.Logger.Infof("Deleted VPC %q", item.name) 97 return nil 98 } 99 if err != nil && getResponse != nil && getResponse.StatusCode == gohttp.StatusInternalServerError { 100 o.Logger.Infof("deleteVPC: internal server error") 101 return nil 102 } 103 104 deleteOptions := o.vpcSvc.NewDeleteVPCOptions(item.id) 105 deleteResponse, err = o.vpcSvc.DeleteVPCWithContext(ctx, deleteOptions) 106 o.Logger.Debugf("deleteVPC: DeleteVPCWithContext returns %+v", deleteResponse) 107 108 if err != nil { 109 return fmt.Errorf("failed to delete vpc %s: %w", item.name, err) 110 } 111 112 o.Logger.Infof("Deleted VPC %q", item.name) 113 o.deletePendingItems(item.typeName, []cloudResource{item}) 114 115 return nil 116 } 117 118 // destroyVPCs removes all vpc resources that have a name prefixed 119 // with the cluster's infra ID. 120 func (o *ClusterUninstaller) destroyVPCs() error { 121 firstPassList, err := o.listVPCs() 122 if err != nil { 123 return err 124 } 125 126 if len(firstPassList.list()) == 0 { 127 return nil 128 } 129 130 items := o.insertPendingItems(vpcTypeName, firstPassList.list()) 131 132 ctx, cancel := o.contextWithTimeout() 133 defer cancel() 134 135 for _, item := range items { 136 select { 137 case <-ctx.Done(): 138 o.Logger.Debugf("destroyVPCs: case <-ctx.Done()") 139 return o.Context.Err() // we're cancelled, abort 140 default: 141 } 142 143 backoff := wait.Backoff{ 144 Duration: 15 * time.Second, 145 Factor: 1.1, 146 Cap: leftInContext(ctx), 147 Steps: math.MaxInt32} 148 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 149 err2 := o.deleteVPC(item) 150 if err2 == nil { 151 return true, err2 152 } 153 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 154 return false, err2 155 }) 156 if err != nil { 157 o.Logger.Fatal("destroyVPCs: ExponentialBackoffWithContext (destroy) returns ", err) 158 } 159 } 160 161 if items = o.getPendingItems(vpcTypeName); len(items) > 0 { 162 for _, item := range items { 163 o.Logger.Debugf("destroyVPCs: found %s in pending items", item.name) 164 } 165 return fmt.Errorf("destroyVPCs: %d undeleted items pending", len(items)) 166 } 167 168 backoff := wait.Backoff{ 169 Duration: 15 * time.Second, 170 Factor: 1.1, 171 Cap: leftInContext(ctx), 172 Steps: math.MaxInt32} 173 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 174 secondPassList, err2 := o.listVPCs() 175 if err2 != nil { 176 return false, err2 177 } 178 if len(secondPassList) == 0 { 179 // We finally don't see any remaining instances! 180 return true, nil 181 } 182 for _, item := range secondPassList { 183 o.Logger.Debugf("destroyVPCs: found %s in second pass", item.name) 184 } 185 return false, nil 186 }) 187 if err != nil { 188 o.Logger.Fatal("destroyVPCs: ExponentialBackoffWithContext (list) returns ", err) 189 } 190 191 return nil 192 }