github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/dns-resource.go (about)

     1  package powervs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  	"net/http"
     8  	"regexp"
     9  	"time"
    10  
    11  	"github.com/IBM-Cloud/bluemix-go/crn"
    12  	"github.com/IBM/go-sdk-core/v5/core"
    13  	"github.com/IBM/networking-go-sdk/resourcerecordsv1"
    14  	"k8s.io/apimachinery/pkg/util/wait"
    15  )
    16  
    17  const (
    18  	ibmDNSRecordTypeName = "ibm dns record"
    19  )
    20  
    21  // listResourceRecords lists DNS Resource records for the cluster.
    22  func (o *ClusterUninstaller) listResourceRecords() (cloudResources, error) {
    23  	o.Logger.Debugf("Listing DNS resource records")
    24  
    25  	ctx, cancel := o.contextWithTimeout()
    26  	defer cancel()
    27  
    28  	select {
    29  	case <-ctx.Done():
    30  		o.Logger.Debugf("listResourceRecords: case <-ctx.Done()")
    31  		return nil, o.Context.Err() // we're cancelled, abort
    32  	default:
    33  	}
    34  
    35  	result := []cloudResource{}
    36  
    37  	dnsCRN, err := crn.Parse(o.DNSInstanceCRN)
    38  	if err != nil {
    39  		return nil, fmt.Errorf("failed to parse DNSInstanceCRN: %w", err)
    40  	}
    41  	records, _, err := o.resourceRecordsSvc.ListResourceRecords(&resourcerecordsv1.ListResourceRecordsOptions{
    42  		InstanceID: &dnsCRN.ServiceInstance,
    43  		DnszoneID:  &o.dnsZoneID,
    44  	})
    45  	if err != nil {
    46  		return nil, fmt.Errorf("failed to list resource records: %w", err)
    47  	}
    48  
    49  	dnsMatcher, err := regexp.Compile(fmt.Sprintf(`.*\Q%s.%s\E$`, o.ClusterName, o.BaseDomain))
    50  	if err != nil {
    51  		return nil, fmt.Errorf("failed to build DNS records matcher: %w", err)
    52  	}
    53  
    54  	for _, record := range records.ResourceRecords {
    55  		// Match all of the cluster's DNS records
    56  		nameMatches := dnsMatcher.Match([]byte(*record.Name))
    57  		if nameMatches {
    58  			o.Logger.Debugf("listResourceRecords: FOUND: %v, %v", *record.ID, *record.Name)
    59  			result = append(result, cloudResource{
    60  				key:      *record.ID,
    61  				name:     *record.Name,
    62  				status:   "",
    63  				typeName: ibmDNSRecordTypeName,
    64  				id:       *record.ID,
    65  			})
    66  		}
    67  	}
    68  	if err != nil {
    69  		return nil, fmt.Errorf("could not retrieve DNS records: %w", err)
    70  	}
    71  	return cloudResources{}.insert(result...), nil
    72  }
    73  
    74  // destroyResourceRecord destroys a Resource Record.
    75  func (o *ClusterUninstaller) destroyResourceRecord(item cloudResource) error {
    76  	var (
    77  		response *core.DetailedResponse
    78  		err      error
    79  	)
    80  
    81  	ctx, cancel := o.contextWithTimeout()
    82  	defer cancel()
    83  
    84  	select {
    85  	case <-ctx.Done():
    86  		o.Logger.Debugf("destroyResourceRecord: case <-ctx.Done()")
    87  		return o.Context.Err() // we're cancelled, abort
    88  	default:
    89  	}
    90  
    91  	if err != nil {
    92  		return fmt.Errorf("failed to delete DNS Resource record %s: %w", item.name, err)
    93  	}
    94  	dnsCRN, err := crn.Parse(o.DNSInstanceCRN)
    95  	if err != nil {
    96  		return fmt.Errorf("failed to parse DNSInstanceCRN: %w", err)
    97  	}
    98  	getOptions := o.resourceRecordsSvc.NewGetResourceRecordOptions(dnsCRN.ServiceInstance, o.dnsZoneID, item.id)
    99  	_, response, err = o.resourceRecordsSvc.GetResourceRecord(getOptions)
   100  
   101  	if err != nil && response != nil && response.StatusCode == http.StatusNotFound {
   102  		// The resource is gone
   103  		o.deletePendingItems(item.typeName, []cloudResource{item})
   104  		o.Logger.Infof("Deleted DNS Resource Record %q", item.name)
   105  		return nil
   106  	}
   107  	if err != nil && response != nil && response.StatusCode == http.StatusInternalServerError {
   108  		o.Logger.Infof("destroyResourceRecord: internal server error")
   109  		return nil
   110  	}
   111  
   112  	deleteOptions := o.resourceRecordsSvc.NewDeleteResourceRecordOptions(dnsCRN.ServiceInstance, o.dnsZoneID, item.id)
   113  
   114  	_, err = o.resourceRecordsSvc.DeleteResourceRecord(deleteOptions)
   115  	if err != nil {
   116  		return fmt.Errorf("failed to delete DNS Resource record %s: %w", item.name, err)
   117  	}
   118  
   119  	o.Logger.Infof("Deleted DNS Resource Record %q", item.name)
   120  	o.deletePendingItems(item.typeName, []cloudResource{item})
   121  
   122  	return nil
   123  }
   124  
   125  // destroyResourceRecords removes all DNS record resources that have a name containing
   126  // the cluster's infra ID.
   127  func (o *ClusterUninstaller) destroyResourceRecords() error {
   128  	if o.resourceRecordsSvc == nil {
   129  		// Install config didn't specify using these resources
   130  		return nil
   131  	}
   132  
   133  	firstPassList, err := o.listResourceRecords()
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	if len(firstPassList.list()) == 0 {
   139  		return nil
   140  	}
   141  
   142  	items := o.insertPendingItems(ibmDNSRecordTypeName, firstPassList.list())
   143  
   144  	ctx, cancel := o.contextWithTimeout()
   145  	defer cancel()
   146  
   147  	for _, item := range items {
   148  		select {
   149  		case <-ctx.Done():
   150  			o.Logger.Debugf("destroyResourceRecords: case <-ctx.Done()")
   151  			return o.Context.Err() // we're cancelled, abort
   152  		default:
   153  		}
   154  
   155  		backoff := wait.Backoff{
   156  			Duration: 15 * time.Second,
   157  			Factor:   1.1,
   158  			Cap:      leftInContext(ctx),
   159  			Steps:    math.MaxInt32}
   160  		err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) {
   161  			err2 := o.destroyResourceRecord(item)
   162  			if err2 == nil {
   163  				return true, err2
   164  			}
   165  			o.errorTracker.suppressWarning(item.key, err2, o.Logger)
   166  			return false, err2
   167  		})
   168  		if err != nil {
   169  			o.Logger.Fatal("destroyResourceRecords: ExponentialBackoffWithContext (destroy) returns ", err)
   170  		}
   171  	}
   172  
   173  	if items = o.getPendingItems(ibmDNSRecordTypeName); len(items) > 0 {
   174  		for _, item := range items {
   175  			o.Logger.Debugf("destroyResourceRecords: found %s in pending items", item.name)
   176  		}
   177  		return fmt.Errorf("destroyResourceRecords: %d undeleted items pending", len(items))
   178  	}
   179  
   180  	backoff := wait.Backoff{
   181  		Duration: 15 * time.Second,
   182  		Factor:   1.1,
   183  		Cap:      leftInContext(ctx),
   184  		Steps:    math.MaxInt32}
   185  	err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) {
   186  		secondPassList, err2 := o.listResourceRecords()
   187  		if err2 != nil {
   188  			return false, err2
   189  		}
   190  		if len(secondPassList) == 0 {
   191  			// We finally don't see any remaining instances!
   192  			return true, nil
   193  		}
   194  		for _, item := range secondPassList {
   195  			o.Logger.Debugf("destroyResourceRecords: found %s in second pass", item.name)
   196  		}
   197  		return false, nil
   198  	})
   199  	if err != nil {
   200  		o.Logger.Fatal("destroyResourceRecords: ExponentialBackoffWithContext (list) returns ", err)
   201  	}
   202  
   203  	return nil
   204  }