github.com/openshift/installer@v1.4.17/pkg/destroy/gcp/dns.go (about) 1 package gcp 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/pkg/errors" 9 "github.com/sirupsen/logrus" 10 dns "google.golang.org/api/dns/v1" 11 "k8s.io/apimachinery/pkg/util/sets" 12 ) 13 14 type dnsZone struct { 15 name string 16 domain string 17 project string 18 } 19 20 func (o *ClusterUninstaller) listDNSZones(ctx context.Context) (private *dnsZone, public []dnsZone, err error) { 21 o.Logger.Debugf("Listing DNS Zones") 22 ctx, cancel := context.WithTimeout(ctx, defaultTimeout) 23 defer cancel() 24 25 projects := []string{o.ProjectID} 26 if o.NetworkProjectID != "" { 27 projects = append(projects, o.NetworkProjectID) 28 } 29 30 for _, project := range projects { 31 req := o.dnsSvc.ManagedZones.List(project).Fields("managedZones(name,dnsName,visibility),nextPageToken") 32 err = req.Pages(ctx, func(response *dns.ManagedZonesListResponse) error { 33 for _, zone := range response.ManagedZones { 34 switch zone.Visibility { 35 case "private": 36 if o.isClusterResource(zone.Name) || (o.PrivateZoneDomain != "" && o.PrivateZoneDomain == zone.DnsName) { 37 private = &dnsZone{name: zone.Name, domain: zone.DnsName, project: project} 38 } 39 default: 40 public = append(public, dnsZone{name: zone.Name, domain: zone.DnsName, project: project}) 41 } 42 } 43 return nil 44 }) 45 if err != nil { 46 err = errors.Wrapf(err, "failed to fetch dns zones") 47 } 48 } 49 return 50 } 51 52 func (o *ClusterUninstaller) deleteDNSZone(ctx context.Context, name string) error { 53 if !o.isClusterResource(name) { 54 o.Logger.Warnf("Skipping deletion of DNS Zone %s, not created by installer", name) 55 return nil 56 } 57 58 o.Logger.Debugf("Deleting DNS zones %s", name) 59 ctx, cancel := context.WithTimeout(ctx, defaultTimeout) 60 defer cancel() 61 err := o.dnsSvc.ManagedZones.Delete(o.ProjectID, name).Context(ctx).Do() 62 if err != nil && !isNoOp(err) { 63 return errors.Wrapf(err, "failed to delete DNS zone %s", name) 64 } 65 o.Logger.Infof("Deleted DNS zone %s", name) 66 return nil 67 } 68 69 func (o *ClusterUninstaller) listDNSZoneRecordSets(ctx context.Context, zone *dnsZone) ([]*dns.ResourceRecordSet, error) { 70 ctx, cancel := context.WithTimeout(ctx, defaultTimeout) 71 defer cancel() 72 req := o.dnsSvc.ResourceRecordSets.List(zone.project, zone.name) 73 result := []*dns.ResourceRecordSet{} 74 err := req.Pages(ctx, func(response *dns.ResourceRecordSetsListResponse) error { 75 result = append(result, response.Rrsets...) 76 return nil 77 }) 78 if err != nil { 79 return nil, errors.Wrapf(err, "failed to fetch resource record sets for zone: %s", zone.name) 80 } 81 return result, nil 82 } 83 84 func (o *ClusterUninstaller) deleteDNSZoneRecordSets(ctx context.Context, zone *dnsZone, recordSets []*dns.ResourceRecordSet) error { 85 change := &dns.Change{} 86 for _, rr := range recordSets { 87 if (rr.Type == "NS" || rr.Type == "SOA") && strings.TrimRight(rr.Name, ".") == strings.TrimRight(zone.domain, ".") { 88 continue 89 } 90 change.Deletions = append(change.Deletions, rr) 91 } 92 if len(change.Deletions) == 0 { 93 return nil 94 } 95 ctx, cancel := context.WithTimeout(ctx, defaultTimeout) 96 defer cancel() 97 o.Logger.Debugf("Deleting %d recordset(s) in zone %s", len(change.Deletions), zone.name) 98 change, err := o.dnsSvc.Changes.Create(zone.project, zone.name, change).ClientOperationId(o.requestID("recordsets", zone.name)).Context(ctx).Do() 99 if err != nil && !isNoOp(err) { 100 o.resetRequestID("recordsets", zone.name) 101 return errors.Wrapf(err, "failed to delete DNS zone %s recordsets", zone.name) 102 } 103 if change != nil && isErrorStatus(int64(change.ServerResponse.HTTPStatusCode)) { 104 o.resetRequestID("recordsets", zone.name) 105 return errors.Errorf("failed to delete DNS zone %s recordsets with code: %d", zone.name, change.ServerResponse.HTTPStatusCode) 106 } 107 o.resetRequestID("recordsets", zone.name) 108 o.Logger.Infof("Deleted %d recordset(s) in zone %s", len(change.Deletions), zone.name) 109 return nil 110 } 111 112 func possibleZoneParents(dnsDomain string) []string { 113 result := []string{} 114 parts := strings.Split(dnsDomain, ".") 115 for len(parts) > 0 { 116 result = append(result, strings.Join(parts, ".")) 117 parts = parts[1:] 118 } 119 return result 120 } 121 122 func getParentDNSZones(dnsDomain string, publicZones []dnsZone, logger logrus.FieldLogger) []*dnsZone { 123 parentZones := []*dnsZone{} 124 125 possibleParents := possibleZoneParents(dnsDomain) 126 for _, parentDomain := range possibleParents { 127 for _, zone := range publicZones { 128 if zone.domain == parentDomain { 129 logger.Debugf("Found parent dns zone: %s", zone.name) 130 parentZones = append(parentZones, &dnsZone{name: zone.name, domain: parentDomain, project: zone.project}) 131 } 132 } 133 } 134 return parentZones 135 } 136 137 // getMatchingRecordSets finds all recordsets in the parent list that match recordsets in the child list 138 // using the record type and record name as keys. 139 func (o *ClusterUninstaller) getMatchingRecordSets(parentRecords, childRecords []*dns.ResourceRecordSet) []*dns.ResourceRecordSet { 140 matchingRecordSets := []*dns.ResourceRecordSet{} 141 recordKey := func(r *dns.ResourceRecordSet) string { 142 return fmt.Sprintf("%s %s", r.Type, r.Name) 143 } 144 childKeys := sets.NewString() 145 for _, record := range childRecords { 146 childKeys.Insert(recordKey(record)) 147 } 148 for _, record := range parentRecords { 149 if childKeys.Has(recordKey(record)) { 150 matchingRecordSets = append(matchingRecordSets, record) 151 } 152 } 153 return matchingRecordSets 154 } 155 156 // destroyDNS deletes DNS resources associated with the cluster. It first finds 157 // the private DNS zone that belongs to the cluster by looking for a zone prefixed 158 // with the cluster's infra ID. It then finds a public zone that is the parent of 159 // the cluster's private zone by searching for zones with a matching domain (in 160 // order from specific to general). If/when a parent DNS zone is found, the records 161 // from the private zone are matched to records in the parent zone (by using type 162 // and name for each record). Matching records are removed from the public zone. 163 // Finally all records are removed from the private zone and the private zone is removed. 164 func (o *ClusterUninstaller) destroyDNS(ctx context.Context) error { 165 privateZone, publicZones, err := o.listDNSZones(ctx) 166 if err != nil { 167 return err 168 } 169 if privateZone == nil { 170 o.Logger.Debugf("Private DNS zone not found") 171 return nil 172 } 173 174 zoneRecordSets, err := o.listDNSZoneRecordSets(ctx, privateZone) 175 if err != nil { 176 return err 177 } 178 179 parentZones := getParentDNSZones(privateZone.domain, publicZones, o.Logger) 180 for _, parentZone := range parentZones { 181 parentRecordSets, err := o.listDNSZoneRecordSets(ctx, parentZone) 182 if err != nil { 183 return err 184 } 185 matchingRecordSets := o.getMatchingRecordSets(parentRecordSets, zoneRecordSets) 186 if len(matchingRecordSets) > 0 { 187 err = o.deleteDNSZoneRecordSets(ctx, parentZone, matchingRecordSets) 188 if err != nil { 189 return err 190 } 191 } 192 } 193 err = o.deleteDNSZoneRecordSets(ctx, privateZone, zoneRecordSets) 194 if err != nil { 195 return err 196 } 197 err = o.deleteDNSZone(ctx, privateZone.name) 198 if err != nil { 199 return err 200 } 201 return nil 202 }