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  }