github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/cloud-transit-gateways.go (about) 1 package powervs 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "strings" 8 "time" 9 10 "github.com/IBM/go-sdk-core/v5/core" 11 "github.com/IBM/networking-go-sdk/transitgatewayapisv1" 12 "k8s.io/apimachinery/pkg/util/wait" 13 ) 14 15 const ( 16 transitGatewayTypeName = "transitGateway" 17 transitGatewayConnectionTypeName = "transitGatewayConnection" 18 ) 19 20 // listTransitGateways lists Transit Gateways in the IBM Cloud. 21 func (o *ClusterUninstaller) listTransitGateways() (cloudResources, error) { 22 o.Logger.Debugf("Listing Transit Gateways (%s)", o.InfraID) 23 24 var ( 25 ctx context.Context 26 cancel func() 27 listTransitGatewaysOptions *transitgatewayapisv1.ListTransitGatewaysOptions 28 gatewayCollection *transitgatewayapisv1.TransitGatewayCollection 29 gateway transitgatewayapisv1.TransitGateway 30 response *core.DetailedResponse 31 err error 32 foundOne = false 33 perPage int64 = 32 34 moreData = true 35 ) 36 37 ctx, cancel = context.WithTimeout(context.Background(), 5*time.Minute) 38 defer cancel() 39 40 listTransitGatewaysOptions = o.tgClient.NewListTransitGatewaysOptions() 41 listTransitGatewaysOptions.Limit = &perPage 42 43 result := []cloudResource{} 44 45 for moreData { 46 // https://github.com/IBM/networking-go-sdk/blob/master/transitgatewayapisv1/transit_gateway_apis_v1.go#L184 47 gatewayCollection, response, err = o.tgClient.ListTransitGatewaysWithContext(ctx, listTransitGatewaysOptions) 48 if err != nil { 49 return nil, fmt.Errorf("failed to list transit gateways: %w and the respose is: %s", err, response) 50 } 51 52 for _, gateway = range gatewayCollection.TransitGateways { 53 if strings.Contains(*gateway.Name, o.InfraID) { 54 foundOne = true 55 o.Logger.Debugf("listTransitGateways: FOUND: %s, %s", *gateway.ID, *gateway.Name) 56 result = append(result, cloudResource{ 57 key: *gateway.ID, 58 name: *gateway.Name, 59 status: "", 60 typeName: transitGatewayTypeName, 61 id: *gateway.ID, 62 }) 63 } 64 } 65 66 if gatewayCollection.First != nil { 67 o.Logger.Debugf("listTransitGateways: First = %+v", *gatewayCollection.First.Href) 68 } else { 69 o.Logger.Debugf("listTransitGateways: First = nil") 70 } 71 if gatewayCollection.Limit != nil { 72 o.Logger.Debugf("listTransitGateways: Limit = %v", *gatewayCollection.Limit) 73 } 74 if gatewayCollection.Next != nil { 75 start, err := gatewayCollection.GetNextStart() 76 if err != nil { 77 o.Logger.Debugf("listTransitGateways: err = %v", err) 78 return nil, fmt.Errorf("listTransitGateways: failed to GetNextStart: %w", err) 79 } 80 if start != nil { 81 o.Logger.Debugf("listTransitGateways: start = %v", *start) 82 listTransitGatewaysOptions.SetStart(*start) 83 } 84 } else { 85 o.Logger.Debugf("listTransitGateways: Next = nil") 86 moreData = false 87 } 88 } 89 if !foundOne { 90 o.Logger.Debugf("listTransitGateways: NO matching transit gateway against: %s", o.InfraID) 91 92 listTransitGatewaysOptions = o.tgClient.NewListTransitGatewaysOptions() 93 listTransitGatewaysOptions.Limit = &perPage 94 moreData = true 95 96 for moreData { 97 gatewayCollection, response, err = o.tgClient.ListTransitGatewaysWithContext(ctx, listTransitGatewaysOptions) 98 if err != nil { 99 return nil, fmt.Errorf("failed to list transit gateways: %w and the respose is: %s", err, response) 100 } 101 for _, gateway = range gatewayCollection.TransitGateways { 102 o.Logger.Debugf("listTransitGateways: FOUND: %s, %s", *gateway.ID, *gateway.Name) 103 } 104 if gatewayCollection.First != nil { 105 o.Logger.Debugf("listTransitGateways: First = %+v", *gatewayCollection.First.Href) 106 } else { 107 o.Logger.Debugf("listTransitGateways: First = nil") 108 } 109 if gatewayCollection.Limit != nil { 110 o.Logger.Debugf("listTransitGateways: Limit = %v", *gatewayCollection.Limit) 111 } 112 if gatewayCollection.Next != nil { 113 start, err := gatewayCollection.GetNextStart() 114 if err != nil { 115 o.Logger.Debugf("listTransitGateways: err = %v", err) 116 return nil, fmt.Errorf("listTransitGateways: failed to GetNextStart: %w", err) 117 } 118 if start != nil { 119 o.Logger.Debugf("listTransitGateways: start = %v", *start) 120 listTransitGatewaysOptions.SetStart(*start) 121 } 122 } else { 123 o.Logger.Debugf("listTransitGateways: Next = nil") 124 moreData = false 125 } 126 } 127 } 128 129 return cloudResources{}.insert(result...), nil 130 } 131 132 // Destroy a specified transit gateway. 133 func (o *ClusterUninstaller) destroyTransitGateway(item cloudResource) error { 134 var ( 135 firstPassList cloudResources 136 137 err error 138 139 items []cloudResource 140 141 ctx context.Context 142 cancel func() 143 144 backoff = wait.Backoff{Duration: 15 * time.Second, 145 Factor: 1.5, 146 Cap: 10 * time.Minute, 147 Steps: math.MaxInt32} 148 149 deleteTransitGatewayOptions *transitgatewayapisv1.DeleteTransitGatewayOptions 150 response *core.DetailedResponse 151 ) 152 153 firstPassList, err = o.listTransitConnections(item) 154 if err != nil { 155 return err 156 } 157 158 items = o.insertPendingItems(transitGatewayConnectionTypeName, firstPassList.list()) 159 160 ctx, cancel = o.contextWithTimeout() 161 defer cancel() 162 163 for _, item := range items { 164 select { 165 case <-o.Context.Done(): 166 o.Logger.Debugf("destroyTransitGateway: case <-o.Context.Done()") 167 return o.Context.Err() // we're cancelled, abort 168 default: 169 } 170 171 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 172 err2 := o.destroyTransitConnection(item) 173 if err2 == nil { 174 return true, err2 175 } 176 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 177 return false, err2 178 }) 179 if err != nil { 180 o.Logger.Fatalf("destroyTransitGateway: ExponentialBackoffWithContext (destroy) returns %v", err) 181 } 182 } 183 184 if items = o.getPendingItems(transitGatewayConnectionTypeName); len(items) > 0 { 185 return fmt.Errorf("destroyTransitGateway: %d undeleted items pending", len(items)) 186 } 187 188 backoff = wait.Backoff{Duration: 15 * time.Second, 189 Factor: 1.5, 190 Cap: 10 * time.Minute, 191 Steps: math.MaxInt32} 192 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 193 var ( 194 secondPassList cloudResources 195 196 err2 error 197 ) 198 199 secondPassList, err2 = o.listTransitConnections(item) 200 if err2 != nil { 201 return false, err2 202 } 203 if len(secondPassList) == 0 { 204 // We finally don't see any remaining instances! 205 return true, nil 206 } 207 for _, item := range secondPassList { 208 o.Logger.Debugf("destroyTransitGateway: found %s in second pass", item.name) 209 } 210 return false, nil 211 }) 212 if err != nil { 213 o.Logger.Fatalf("destroyTransitGateway: ExponentialBackoffWithContext (list) returns %v", err) 214 } 215 216 // We can delete the transit gateway now! 217 deleteTransitGatewayOptions = o.tgClient.NewDeleteTransitGatewayOptions(item.id) 218 219 response, err = o.tgClient.DeleteTransitGatewayWithContext(ctx, deleteTransitGatewayOptions) 220 if err != nil { 221 o.Logger.Fatalf("destroyTransitGateway: DeleteTransitGatewayWithContext returns %v with response %v", err, response) 222 } 223 224 o.deletePendingItems(item.typeName, []cloudResource{item}) 225 o.Logger.Infof("Deleted Transit Gateway %q", item.name) 226 227 return nil 228 } 229 230 // Destroy a specified transit gateway connection. 231 func (o *ClusterUninstaller) destroyTransitConnection(item cloudResource) error { 232 var ( 233 ctx context.Context 234 cancel func() 235 236 deleteTransitGatewayConnectionOptions *transitgatewayapisv1.DeleteTransitGatewayConnectionOptions 237 response *core.DetailedResponse 238 err error 239 ) 240 241 ctx, cancel = o.contextWithTimeout() 242 defer cancel() 243 244 // ...Options(transitGatewayID string, id string) 245 // NOTE: item.status is reused as the parent transit gateway id! 246 deleteTransitGatewayConnectionOptions = o.tgClient.NewDeleteTransitGatewayConnectionOptions(item.status, item.id) 247 248 response, err = o.tgClient.DeleteTransitGatewayConnectionWithContext(ctx, deleteTransitGatewayConnectionOptions) 249 if err != nil { 250 o.Logger.Fatalf("destroyTransitConnection: DeleteTransitGatewayConnectionWithContext returns %v with response %v", err, response) 251 } 252 253 o.deletePendingItems(item.typeName, []cloudResource{item}) 254 o.Logger.Infof("Deleted Transit Gateway Connection %q", item.name) 255 256 return nil 257 } 258 259 // listTransitGateways lists Transit Connections for a Transit Gateway in the IBM Cloud. 260 func (o *ClusterUninstaller) listTransitConnections(item cloudResource) (cloudResources, error) { 261 o.Logger.Debugf("Listing Transit Gateways Connections (%s)", item.name) 262 263 var ( 264 ctx context.Context 265 cancel func() 266 listConnectionsOptions *transitgatewayapisv1.ListConnectionsOptions 267 transitConnectionCollections *transitgatewayapisv1.TransitConnectionCollection 268 transitConnection transitgatewayapisv1.TransitConnection 269 response *core.DetailedResponse 270 err error 271 foundOne = false 272 perPage int64 = 32 273 moreData = true 274 ) 275 276 ctx, cancel = o.contextWithTimeout() 277 defer cancel() 278 279 listConnectionsOptions = o.tgClient.NewListConnectionsOptions() 280 listConnectionsOptions.SetLimit(perPage) 281 listConnectionsOptions.SetNetworkID("") 282 283 result := []cloudResource{} 284 285 for moreData { 286 transitConnectionCollections, response, err = o.tgClient.ListConnectionsWithContext(ctx, listConnectionsOptions) 287 if err != nil { 288 o.Logger.Debugf("listTransitConnections: ListConnections returns %v and the response is: %s", err, response) 289 return nil, err 290 } 291 for _, transitConnection = range transitConnectionCollections.Connections { 292 if !strings.Contains(*transitConnection.TransitGateway.Name, o.InfraID) { 293 continue 294 } 295 296 foundOne = true 297 o.Logger.Debugf("listTransitConnections: FOUND: %s, %s, %s", *transitConnection.ID, *transitConnection.Name, *transitConnection.TransitGateway.Name) 298 result = append(result, cloudResource{ 299 key: *transitConnection.ID, 300 name: *transitConnection.Name, 301 status: *transitConnection.TransitGateway.ID, 302 typeName: transitGatewayConnectionTypeName, 303 id: *transitConnection.ID, 304 }) 305 } 306 307 if transitConnectionCollections.First != nil { 308 o.Logger.Debugf("listTransitConnections: First = %+v", *transitConnectionCollections.First) 309 } else { 310 o.Logger.Debugf("listTransitConnections: First = nil") 311 } 312 if transitConnectionCollections.Limit != nil { 313 o.Logger.Debugf("listTransitConnections: Limit = %v", *transitConnectionCollections.Limit) 314 } 315 if transitConnectionCollections.Next != nil { 316 start, err := transitConnectionCollections.GetNextStart() 317 if err != nil { 318 o.Logger.Debugf("listTransitConnections: err = %v", err) 319 return nil, fmt.Errorf("listTransitConnections: failed to GetNextStart: %w", err) 320 } 321 if start != nil { 322 o.Logger.Debugf("listTransitConnections: start = %v", *start) 323 listConnectionsOptions.SetStart(*start) 324 } 325 } else { 326 o.Logger.Debugf("listTransitConnections: Next = nil") 327 moreData = false 328 } 329 } 330 if !foundOne { 331 o.Logger.Debugf("listTransitConnections: NO matching transit connections against: %s", o.InfraID) 332 333 listConnectionsOptions = o.tgClient.NewListConnectionsOptions() 334 listConnectionsOptions.SetLimit(perPage) 335 listConnectionsOptions.SetNetworkID("") 336 moreData = true 337 338 for moreData { 339 transitConnectionCollections, response, err = o.tgClient.ListConnectionsWithContext(ctx, listConnectionsOptions) 340 if err != nil { 341 o.Logger.Debugf("listTransitConnections: ListConnections returns %v and the response is: %s", err, response) 342 return nil, err 343 } 344 for _, transitConnection = range transitConnectionCollections.Connections { 345 o.Logger.Debugf("listTransitConnections: FOUND: %s, %s, %s", *transitConnection.ID, *transitConnection.Name, *transitConnection.TransitGateway.Name) 346 } 347 if transitConnectionCollections.First != nil { 348 o.Logger.Debugf("listTransitConnections: First = %+v", *transitConnectionCollections.First) 349 } else { 350 o.Logger.Debugf("listTransitConnections: First = nil") 351 } 352 if transitConnectionCollections.Limit != nil { 353 o.Logger.Debugf("listTransitConnections: Limit = %v", *transitConnectionCollections.Limit) 354 } 355 if transitConnectionCollections.Next != nil { 356 start, err := transitConnectionCollections.GetNextStart() 357 if err != nil { 358 o.Logger.Debugf("listTransitConnections: err = %v", err) 359 return nil, fmt.Errorf("listTransitConnections: failed to GetNextStart: %w", err) 360 } 361 if start != nil { 362 o.Logger.Debugf("listTransitConnections: start = %v", *start) 363 listConnectionsOptions.SetStart(*start) 364 } 365 } else { 366 o.Logger.Debugf("listTransitConnections: Next = nil") 367 moreData = false 368 } 369 } 370 } 371 372 return cloudResources{}.insert(result...), nil 373 } 374 375 // destroyTransitGateways searches for transit gateways that have a name that starts with 376 // the cluster's infra ID. 377 func (o *ClusterUninstaller) destroyTransitGateways() error { 378 var ( 379 firstPassList cloudResources 380 381 err error 382 383 items []cloudResource 384 385 ctx context.Context 386 cancel func() 387 388 backoff = wait.Backoff{Duration: 15 * time.Second, 389 Factor: 1.5, 390 Cap: 10 * time.Minute, 391 Steps: math.MaxInt32} 392 ) 393 394 firstPassList, err = o.listTransitGateways() 395 if err != nil { 396 return err 397 } 398 399 items = o.insertPendingItems(transitGatewayTypeName, firstPassList.list()) 400 401 ctx, cancel = o.contextWithTimeout() 402 defer cancel() 403 404 for _, item := range items { 405 select { 406 case <-o.Context.Done(): 407 o.Logger.Debugf("destroyTransitGateways: case <-o.Context.Done()") 408 return o.Context.Err() // we're cancelled, abort 409 default: 410 } 411 412 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 413 err2 := o.destroyTransitGateway(item) 414 if err2 == nil { 415 return true, err2 416 } 417 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 418 return false, err2 419 }) 420 if err != nil { 421 o.Logger.Fatalf("destroyTransitGateways: ExponentialBackoffWithContext (destroy) returns %v", err) 422 } 423 } 424 425 if items = o.getPendingItems(transitGatewayTypeName); len(items) > 0 { 426 return fmt.Errorf("destroyTransitGateways: %d undeleted items pending", len(items)) 427 } 428 429 backoff = wait.Backoff{Duration: 15 * time.Second, 430 Factor: 1.5, 431 Cap: 10 * time.Minute, 432 Steps: math.MaxInt32} 433 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 434 var ( 435 secondPassList cloudResources 436 437 err2 error 438 ) 439 440 secondPassList, err2 = o.listTransitGateways() 441 if err2 != nil { 442 return false, err2 443 } 444 if len(secondPassList) == 0 { 445 // We finally don't see any remaining instances! 446 return true, nil 447 } 448 for _, item := range secondPassList { 449 o.Logger.Debugf("destroyTransitGateways: found %s in second pass", item.name) 450 } 451 return false, nil 452 }) 453 if err != nil { 454 o.Logger.Fatalf("destroyTransitGateways: ExponentialBackoffWithContext (list) returns %v", err) 455 } 456 457 return nil 458 }