provider/pdns.go
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 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
}
// ConvertEndpointsToZones marshals endpoints into pdns compatible Zone structs