func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changetype pdnsChangeType) (zonelist []pgo.Zone, _ error) {

	zonelist = []pgo.Zone{}
	endpoints := make([]*endpoint.Endpoint, len(eps))
	copy(endpoints, eps)

	// Sort the endpoints array so we have deterministic inserts
	sort.SliceStable(endpoints,
		func(i, j int) bool {
			// We only care about sorting endpoints with the same dnsname
			if endpoints[i].DNSName == endpoints[j].DNSName {
				return endpoints[i].RecordType < endpoints[j].RecordType
			}
			return endpoints[i].DNSName < endpoints[j].DNSName
		})

	zones, _, err := p.client.ListZones()
	if err != nil {
		return nil, err
	}
	filteredZones, residualZones := p.client.PartitionZones(zones)

	// Sort the zone by length of the name in descending order, we use this
	// property later to ensure we add a record to the longest matching zone

	sort.SliceStable(filteredZones, func(i, j int) bool { return len(filteredZones[i].Name) > len(filteredZones[j].Name) })

	// NOTE: Complexity of this loop is O(FilteredZones*Endpoints).
	// A possibly faster implementation would be a search of the reversed
	// DNSName in a trie of Zone names, which should be O(Endpoints), but at this point it's not
	// necessary.
	for _, zone := range filteredZones {
		zone.Rrsets = []pgo.RrSet{}
		for i := 0; i < len(endpoints); {
			ep := endpoints[i]
			dnsname := ensureTrailingDot(ep.DNSName)
			if dnsname == zone.Name || strings.HasSuffix(dnsname, "."+zone.Name) {
				// The assumption here is that there will only ever be one target
				// per (ep.DNSName, ep.RecordType) tuple, which holds true for
				// external-dns v5.0.0-alpha onwards
				records := []pgo.Record{}
				for _, t := range ep.Targets {
					if "CNAME" == ep.RecordType {
						t = ensureTrailingDot(t)
					}

					records = append(records, pgo.Record{Content: t})
				}
				rrset := pgo.RrSet{
					Name:       dnsname,
					Type_:      ep.RecordType,
					Records:    records,
					Changetype: string(changetype),
				}

				// DELETEs explicitly forbid a TTL, therefore only PATCHes need the TTL
				if changetype == PdnsReplace {
					if int64(ep.RecordTTL) > int64(math.MaxInt32) {
						return nil, errors.New("Value of record TTL overflows, limited to int32")
					}
					if ep.RecordTTL == 0 {
						// No TTL was specified for the record, we use the default
						rrset.Ttl = int32(defaultTTL)
					} else {
						rrset.Ttl = int32(ep.RecordTTL)
					}
				}

				zone.Rrsets = append(zone.Rrsets, rrset)

				// "pop" endpoint if it's matched
				endpoints = append(endpoints[0:i], endpoints[i+1:]...)
			} else {
				// If we didn't pop anything, we move to the next item in the list
				i++
			}

		}

		if len(zone.Rrsets) > 0 {
			zonelist = append(zonelist, zone)
		}

	}

	// residualZones is unsorted by name length like its counterpart
	// since we only care to remove endpoints that do not match domain filter
	for _, zone := range residualZones {
		for i := 0; i < len(endpoints); {
			ep := endpoints[i]
			dnsname := ensureTrailingDot(ep.DNSName)
			if dnsname == zone.Name || strings.HasSuffix(dnsname, "."+zone.Name) {
				// "pop" endpoint if it's matched to a residual zone... essentially a no-op
				log.Debugf("Ignoring Endpoint because it was matched to a zone that was not specified within Domain Filter(s): %s", dnsname)
				endpoints = append(endpoints[0:i], endpoints[i+1:]...)
			} else {
				i++
			}
		}
	}

	// If we still have some endpoints left, it means we couldn't find a matching zone (filtered or residual) for them
	// We warn instead of hard fail here because we don't want a misconfig to cause everything to go down
	if len(endpoints) > 0 {
		log.Warnf("No matching zones were found for the following endpoints: %+v", endpoints)
	}

	log.Debugf("Zone List generated from Endpoints: %+v", zonelist)

	return zonelist, nil
}
func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changetype pdnsChangeType) (zonelist []pgo.Zone, _ error) {

	zonelist = []pgo.Zone{}
	endpoints := make([]*endpoint.Endpoint, len(eps))
	copy(endpoints, eps)

	
	sort.SliceStable(endpoints,
		func(i, j int) bool {
			
			if endpoints[i].DNSName == endpoints[j].DNSName {
				return endpoints[i].RecordType < endpoints[j].RecordType
			}
			return endpoints[i].DNSName < endpoints[j].DNSName
		})

	zones, _, err := p.client.ListZones()
	if err != nil {
		return nil, err
	}
	filteredZones, residualZones := p.client.PartitionZones(zones)

	
	

	sort.SliceStable(filteredZones, func(i, j int) bool { return len(filteredZones[i].Name) > len(filteredZones[j].Name) })

	
	
	
	
	for _, zone := range filteredZones {
		zone.Rrsets = []pgo.RrSet{}
		for i := 0; i < len(endpoints); {
			ep := endpoints[i]
			dnsname := ensureTrailingDot(ep.DNSName)
			if dnsname == zone.Name || strings.HasSuffix(dnsname, "."+zone.Name) {
				
				
				
				records := []pgo.Record{}
				for _, t := range ep.Targets {
					if "CNAME" == ep.RecordType {
						t = ensureTrailingDot(t)
					}

					records = append(records, pgo.Record{Content: t})
				}
				rrset := pgo.RrSet{
					Name:       dnsname,
					Type_:      ep.RecordType,
					Records:    records,
					Changetype: string(changetype),
				}

				
				if changetype == PdnsReplace {
					if int64(ep.RecordTTL) > int64(math.MaxInt32) {
						return nil, errors.New("Value of record TTL overflows, limited to int32")
					}
					if ep.RecordTTL == 0 {
						
						rrset.Ttl = int32(defaultTTL)
					} else {
						rrset.Ttl = int32(ep.RecordTTL)
					}
				}

				zone.Rrsets = append(zone.Rrsets, rrset)

				
				endpoints = append(endpoints[0:i], endpoints[i+1:]...)
			} else {
				
				i++
			}

		}

		if len(zone.Rrsets) > 0 {
			zonelist = append(zonelist, zone)
		}

	}

	
	
	for _, zone := range residualZones {
		for i := 0; i < len(endpoints); {
			ep := endpoints[i]
			dnsname := ensureTrailingDot(ep.DNSName)
			if dnsname == zone.Name || strings.HasSuffix(dnsname, "."+zone.Name) {
				
				log.Debugf("Ignoring Endpoint because it was matched to a zone that was not specified within Domain Filter(s): %s", dnsname)
				endpoints = append(endpoints[0:i], endpoints[i+1:]...)
			} else {
				i++
			}
		}
	}

	
	
	if len(endpoints) > 0 {
		log.Warnf("No matching zones were found for the following endpoints: %+v", endpoints)
	}

	log.Debugf("Zone List generated from Endpoints: %+v", zonelist)

	return zonelist, nil
}
func ( p * PDNSProvider ) ConvertEndpointsToZones ( eps [ ] * endpoint . Endpoint , changetype pdnsChangeType ) ( zonelist [ ] pgo . Zone , _ error ) { zonelist = [ ] pgo . Zone { } endpoints := make ( [ ] * endpoint . Endpoint , len ( eps ) ) copy ( endpoints , eps ) // Sort the endpoints array so we have deterministic inserts sort . SliceStable ( endpoints , func ( i , j int ) bool { // We only care about sorting endpoints with the same dnsname if endpoints [ i ] . DNSName == endpoints [ j ] . DNSName { return endpoints [ i ] . RecordType < endpoints [ j ] . RecordType } return endpoints [ i ] . DNSName < endpoints [ j ] . DNSName } ) zones , _ , err := p . client . ListZones ( ) if err != nil { return nil , err } filteredZones , residualZones := p . client . PartitionZones ( zones ) // Sort the zone by length of the name in descending order, we use this // property later to ensure we add a record to the longest matching zone sort . SliceStable ( filteredZones , func ( i , j int ) bool { return len ( filteredZones [ i ] . Name ) > len ( filteredZones [ j ] . Name ) } ) // NOTE: Complexity of this loop is O(FilteredZones*Endpoints). // A possibly faster implementation would be a search of the reversed // DNSName in a trie of Zone names, which should be O(Endpoints), but at this point it's not // necessary. for _ , zone := range filteredZones { zone . Rrsets = [ ] pgo . RrSet { } for i := 0 ; i < len ( endpoints ) ; { ep := endpoints [ i ] dnsname := ensureTrailingDot ( ep . DNSName ) if dnsname == zone . Name || strings . HasSuffix ( dnsname , " " + zone . Name ) { // The assumption here is that there will only ever be one target // per (ep.DNSName, ep.RecordType) tuple, which holds true for // external-dns v5.0.0-alpha onwards records := [ ] pgo . Record { } for _ , t := range ep . Targets { if " " == ep . RecordType { t = ensureTrailingDot ( t ) } records = append ( records , pgo . Record { Content : t } ) } rrset := pgo . RrSet { Name : dnsname , Type_ : ep . RecordType , Records : records , Changetype : string ( changetype ) , } // DELETEs explicitly forbid a TTL, therefore only PATCHes need the TTL if changetype == PdnsReplace { if int64 ( ep . RecordTTL ) > int64 ( math . MaxInt32 ) { return nil , errors . New ( " " ) } if ep . RecordTTL == 0 { // No TTL was specified for the record, we use the default rrset . Ttl = int32 ( defaultTTL ) } else { rrset . Ttl = int32 ( ep . RecordTTL ) } } zone . Rrsets = append ( zone . Rrsets , rrset ) // "pop" endpoint if it's matched endpoints = append ( endpoints [ 0 : i ] , endpoints [ i + 1 : ] ... ) } else { // If we didn't pop anything, we move to the next item in the list i ++ } } if len ( zone . Rrsets ) > 0 { zonelist = append ( zonelist , zone ) } } // residualZones is unsorted by name length like its counterpart // since we only care to remove endpoints that do not match domain filter for _ , zone := range residualZones { for i := 0 ; i < len ( endpoints ) ; { ep := endpoints [ i ] dnsname := ensureTrailingDot ( ep . DNSName ) if dnsname == zone . Name || strings . HasSuffix ( dnsname , " " + zone . Name ) { // "pop" endpoint if it's matched to a residual zone... essentially a no-op log . Debugf ( " " , dnsname ) endpoints = append ( endpoints [ 0 : i ] , endpoints [ i + 1 : ] ... ) } else { i ++ } } } // If we still have some endpoints left, it means we couldn't find a matching zone (filtered or residual) for them // We warn instead of hard fail here because we don't want a misconfig to cause everything to go down if len ( endpoints ) > 0 { log . Warnf ( " " , endpoints ) } log . Debugf ( " " , zonelist ) return zonelist , nil }
// ConvertEndpointsToZones marshals endpoints into pdns compatible Zone structs
ConvertEndpointsToZones marshals endpoints into pdns compatible Zone structs