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 }