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  }