provider/linode.go
func (p *LinodeProvider) ApplyChanges(changes *plan.Changes) error {
	recordsByZoneID := make(map[string][]*linodego.DomainRecord)

	zones, err := p.fetchZones()

	if err != nil {
		return err
	}

	zonesByID := make(map[string]*linodego.Domain)

	zoneNameIDMapper := zoneIDName{}

	for _, z := range zones {
		zoneNameIDMapper.Add(strconv.Itoa(z.ID), z.Domain)
		zonesByID[strconv.Itoa(z.ID)] = z
	}

	// Fetch records for each zone
	for _, zone := range zones {
		records, err := p.fetchRecords(zone.ID)

		if err != nil {
			return err
		}

		recordsByZoneID[strconv.Itoa(zone.ID)] = append(recordsByZoneID[strconv.Itoa(zone.ID)], records...)
	}

	createsByZone := endpointsByZone(zoneNameIDMapper, changes.Create)
	updatesByZone := endpointsByZone(zoneNameIDMapper, changes.UpdateNew)
	deletesByZone := endpointsByZone(zoneNameIDMapper, changes.Delete)

	var linodeCreates []*LinodeChangeCreate
	var linodeUpdates []*LinodeChangeUpdate
	var linodeDeletes []*LinodeChangeDelete

	// Generate Creates
	for zoneID, creates := range createsByZone {
		zone := zonesByID[zoneID]

		if len(creates) == 0 {
			log.WithFields(log.Fields{
				"zoneID":   zoneID,
				"zoneName": zone.Domain,
			}).Debug("Skipping Zone, no creates found.")
			continue
		}

		records := recordsByZoneID[zoneID]

		for _, ep := range creates {
			matchedRecords := getRecordID(records, zone, ep)

			if len(matchedRecords) != 0 {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"zoneName":   zone.Domain,
					"dnsName":    ep.DNSName,
					"recordType": ep.RecordType,
				}).Warn("Records found which should not exist")
			}

			recordType, err := convertRecordType(ep.RecordType)

			if err != nil {
				return err
			}

			for _, target := range ep.Targets {
				linodeCreates = append(linodeCreates, &LinodeChangeCreate{
					Domain: zone,
					Options: linodego.DomainRecordCreateOptions{
						Target:   target,
						Name:     getStrippedRecordName(zone, ep),
						Type:     recordType,
						Weight:   getWeight(),
						Port:     getPort(),
						Priority: getPriority(),
						TTLSec:   int(ep.RecordTTL),
					},
				})
			}
		}
	}

	// Generate Updates
	for zoneID, updates := range updatesByZone {
		zone := zonesByID[zoneID]

		if len(updates) == 0 {
			log.WithFields(log.Fields{
				"zoneID":   zoneID,
				"zoneName": zone.Domain,
			}).Debug("Skipping Zone, no updates found.")
			continue
		}

		records := recordsByZoneID[zoneID]

		for _, ep := range updates {
			matchedRecords := getRecordID(records, zone, ep)

			if len(matchedRecords) == 0 {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"dnsName":    ep.DNSName,
					"zoneName":   zone.Domain,
					"recordType": ep.RecordType,
				}).Warn("Update Records not found.")
			}

			recordType, err := convertRecordType(ep.RecordType)

			if err != nil {
				return err
			}

			matchedRecordsByTarget := make(map[string]*linodego.DomainRecord)

			for _, record := range matchedRecords {
				matchedRecordsByTarget[record.Target] = record
			}

			for _, target := range ep.Targets {
				if record, ok := matchedRecordsByTarget[target]; ok {
					log.WithFields(log.Fields{
						"zoneID":     zoneID,
						"dnsName":    ep.DNSName,
						"zoneName":   zone.Domain,
						"recordType": ep.RecordType,
						"target":     target,
					}).Warn("Updating Existing Target")

					linodeUpdates = append(linodeUpdates, &LinodeChangeUpdate{
						Domain:       zone,
						DomainRecord: record,
						Options: linodego.DomainRecordUpdateOptions{
							Target:   target,
							Name:     getStrippedRecordName(zone, ep),
							Type:     recordType,
							Weight:   getWeight(),
							Port:     getPort(),
							Priority: getPriority(),
							TTLSec:   int(ep.RecordTTL),
						},
					})

					delete(matchedRecordsByTarget, target)
				} else {
					// Record did not previously exist, create new 'target'
					log.WithFields(log.Fields{
						"zoneID":     zoneID,
						"dnsName":    ep.DNSName,
						"zoneName":   zone.Domain,
						"recordType": ep.RecordType,
						"target":     target,
					}).Warn("Creating New Target")

					linodeCreates = append(linodeCreates, &LinodeChangeCreate{
						Domain: zone,
						Options: linodego.DomainRecordCreateOptions{
							Target:   target,
							Name:     getStrippedRecordName(zone, ep),
							Type:     recordType,
							Weight:   getWeight(),
							Port:     getPort(),
							Priority: getPriority(),
							TTLSec:   int(ep.RecordTTL),
						},
					})
				}
			}

			// Any remaining records have been removed, delete them
			for _, record := range matchedRecordsByTarget {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"dnsName":    ep.DNSName,
					"zoneName":   zone.Domain,
					"recordType": ep.RecordType,
					"target":     record.Target,
				}).Warn("Deleting Target")

				linodeDeletes = append(linodeDeletes, &LinodeChangeDelete{
					Domain:       zone,
					DomainRecord: record,
				})
			}

		}
	}

	// Generate Deletes
	for zoneID, deletes := range deletesByZone {
		zone := zonesByID[zoneID]

		if len(deletes) == 0 {
			log.WithFields(log.Fields{
				"zoneID":   zoneID,
				"zoneName": zone.Domain,
			}).Debug("Skipping Zone, no deletes found.")
			continue
		}

		records := recordsByZoneID[zoneID]

		for _, ep := range deletes {
			matchedRecords := getRecordID(records, zone, ep)

			if len(matchedRecords) == 0 {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"dnsName":    ep.DNSName,
					"zoneName":   zone.Domain,
					"recordType": ep.RecordType,
				}).Warn("Records to Delete not found.")
			}

			for _, record := range matchedRecords {
				linodeDeletes = append(linodeDeletes, &LinodeChangeDelete{
					Domain:       zone,
					DomainRecord: record,
				})
			}
		}
	}

	return p.submitChanges(LinodeChanges{
		Creates: linodeCreates,
		Deletes: linodeDeletes,
		Updates: linodeUpdates,
	})
}
func (p *LinodeProvider) ApplyChanges(changes *plan.Changes) error {
	recordsByZoneID := make(map[string][]*linodego.DomainRecord)

	zones, err := p.fetchZones()

	if err != nil {
		return err
	}

	zonesByID := make(map[string]*linodego.Domain)

	zoneNameIDMapper := zoneIDName{}

	for _, z := range zones {
		zoneNameIDMapper.Add(strconv.Itoa(z.ID), z.Domain)
		zonesByID[strconv.Itoa(z.ID)] = z
	}

	// Fetch records for each zone
	for _, zone := range zones {
		records, err := p.fetchRecords(zone.ID)

		if err != nil {
			return err
		}

		recordsByZoneID[strconv.Itoa(zone.ID)] = append(recordsByZoneID[strconv.Itoa(zone.ID)], records...)
	}

	createsByZone := endpointsByZone(zoneNameIDMapper, changes.Create)
	updatesByZone := endpointsByZone(zoneNameIDMapper, changes.UpdateNew)
	deletesByZone := endpointsByZone(zoneNameIDMapper, changes.Delete)

	var linodeCreates []*LinodeChangeCreate
	var linodeUpdates []*LinodeChangeUpdate
	var linodeDeletes []*LinodeChangeDelete

	// Generate Creates
	for zoneID, creates := range createsByZone {
		zone := zonesByID[zoneID]

		if len(creates) == 0 {
			log.WithFields(log.Fields{
				"zoneID":   zoneID,
				"zoneName": zone.Domain,
			}).Debug("Skipping Zone, no creates found.")
			continue
		}

		records := recordsByZoneID[zoneID]

		for _, ep := range creates {
			matchedRecords := getRecordID(records, zone, ep)

			if len(matchedRecords) != 0 {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"zoneName":   zone.Domain,
					"dnsName":    ep.DNSName,
					"recordType": ep.RecordType,
				}).Warn("Records found which should not exist")
			}

			recordType, err := convertRecordType(ep.RecordType)

			if err != nil {
				return err
			}

			for _, target := range ep.Targets {
				linodeCreates = append(linodeCreates, &LinodeChangeCreate{
					Domain: zone,
					Options: linodego.DomainRecordCreateOptions{
						Target:   target,
						Name:     getStrippedRecordName(zone, ep),
						Type:     recordType,
						Weight:   getWeight(),
						Port:     getPort(),
						Priority: getPriority(),
						TTLSec:   int(ep.RecordTTL),
					},
				})
			}
		}
	}

	// Generate Updates
	for zoneID, updates := range updatesByZone {
		zone := zonesByID[zoneID]

		if len(updates) == 0 {
			log.WithFields(log.Fields{
				"zoneID":   zoneID,
				"zoneName": zone.Domain,
			}).Debug("Skipping Zone, no updates found.")
			continue
		}

		records := recordsByZoneID[zoneID]

		for _, ep := range updates {
			matchedRecords := getRecordID(records, zone, ep)

			if len(matchedRecords) == 0 {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"dnsName":    ep.DNSName,
					"zoneName":   zone.Domain,
					"recordType": ep.RecordType,
				}).Warn("Update Records not found.")
			}

			recordType, err := convertRecordType(ep.RecordType)

			if err != nil {
				return err
			}

			matchedRecordsByTarget := make(map[string]*linodego.DomainRecord)

			for _, record := range matchedRecords {
				matchedRecordsByTarget[record.Target] = record
			}

			for _, target := range ep.Targets {
				if record, ok := matchedRecordsByTarget[target]; ok {
					log.WithFields(log.Fields{
						"zoneID":     zoneID,
						"dnsName":    ep.DNSName,
						"zoneName":   zone.Domain,
						"recordType": ep.RecordType,
						"target":     target,
					}).Warn("Updating Existing Target")

					linodeUpdates = append(linodeUpdates, &LinodeChangeUpdate{
						Domain:       zone,
						DomainRecord: record,
						Options: linodego.DomainRecordUpdateOptions{
							Target:   target,
							Name:     getStrippedRecordName(zone, ep),
							Type:     recordType,
							Weight:   getWeight(),
							Port:     getPort(),
							Priority: getPriority(),
							TTLSec:   int(ep.RecordTTL),
						},
					})

					delete(matchedRecordsByTarget, target)
				} else {
					// Record did not previously exist, create new 'target'
					log.WithFields(log.Fields{
						"zoneID":     zoneID,
						"dnsName":    ep.DNSName,
						"zoneName":   zone.Domain,
						"recordType": ep.RecordType,
						"target":     target,
					}).Warn("Creating New Target")

					linodeCreates = append(linodeCreates, &LinodeChangeCreate{
						Domain: zone,
						Options: linodego.DomainRecordCreateOptions{
							Target:   target,
							Name:     getStrippedRecordName(zone, ep),
							Type:     recordType,
							Weight:   getWeight(),
							Port:     getPort(),
							Priority: getPriority(),
							TTLSec:   int(ep.RecordTTL),
						},
					})
				}
			}

			// Any remaining records have been removed, delete them
			for _, record := range matchedRecordsByTarget {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"dnsName":    ep.DNSName,
					"zoneName":   zone.Domain,
					"recordType": ep.RecordType,
					"target":     record.Target,
				}).Warn("Deleting Target")

				linodeDeletes = append(linodeDeletes, &LinodeChangeDelete{
					Domain:       zone,
					DomainRecord: record,
				})
			}

		}
	}

	// Generate Deletes
	for zoneID, deletes := range deletesByZone {
		zone := zonesByID[zoneID]

		if len(deletes) == 0 {
			log.WithFields(log.Fields{
				"zoneID":   zoneID,
				"zoneName": zone.Domain,
			}).Debug("Skipping Zone, no deletes found.")
			continue
		}

		records := recordsByZoneID[zoneID]

		for _, ep := range deletes {
			matchedRecords := getRecordID(records, zone, ep)

			if len(matchedRecords) == 0 {
				log.WithFields(log.Fields{
					"zoneID":     zoneID,
					"dnsName":    ep.DNSName,
					"zoneName":   zone.Domain,
					"recordType": ep.RecordType,
				}).Warn("Records to Delete not found.")
			}

			for _, record := range matchedRecords {
				linodeDeletes = append(linodeDeletes, &LinodeChangeDelete{
					Domain:       zone,
					DomainRecord: record,
				})
			}
		}
	}

	return p.submitChanges(LinodeChanges{
		Creates: linodeCreates,
		Deletes: linodeDeletes,
		Updates: linodeUpdates,
	})
}
func ( p * LinodeProvider ) ApplyChanges ( changes * plan . Changes ) error { recordsByZoneID := make ( map [ string ] [ ] * linodego . DomainRecord ) zones , err := p . fetchZones ( ) if err != nil { return err } zonesByID := make ( map [ string ] * linodego . Domain ) zoneNameIDMapper := zoneIDName { } for _ , z := range zones { zoneNameIDMapper . Add ( strconv . Itoa ( z . ID ) , z . Domain ) zonesByID [ strconv . Itoa ( z . ID ) ] = z } // Fetch records for each zone for _ , zone := range zones { records , err := p . fetchRecords ( zone . ID ) if err != nil { return err } recordsByZoneID [ strconv . Itoa ( zone . ID ) ] = append ( recordsByZoneID [ strconv . Itoa ( zone . ID ) ] , records ... ) } createsByZone := endpointsByZone ( zoneNameIDMapper , changes . Create ) updatesByZone := endpointsByZone ( zoneNameIDMapper , changes . UpdateNew ) deletesByZone := endpointsByZone ( zoneNameIDMapper , changes . Delete ) var linodeCreates [ ] * LinodeChangeCreate var linodeUpdates [ ] * LinodeChangeUpdate var linodeDeletes [ ] * LinodeChangeDelete // Generate Creates for zoneID , creates := range createsByZone { zone := zonesByID [ zoneID ] if len ( creates ) == 0 { log . WithFields ( log . Fields { " " : zoneID , " " : zone . Domain , } ) . Debug ( " " ) continue } records := recordsByZoneID [ zoneID ] for _ , ep := range creates { matchedRecords := getRecordID ( records , zone , ep ) if len ( matchedRecords ) != 0 { log . WithFields ( log . Fields { " " : zoneID , " " : zone . Domain , " " : ep . DNSName , " " : ep . RecordType , } ) . Warn ( " " ) } recordType , err := convertRecordType ( ep . RecordType ) if err != nil { return err } for _ , target := range ep . Targets { linodeCreates = append ( linodeCreates , & LinodeChangeCreate { Domain : zone , Options : linodego . DomainRecordCreateOptions { Target : target , Name : getStrippedRecordName ( zone , ep ) , Type : recordType , Weight : getWeight ( ) , Port : getPort ( ) , Priority : getPriority ( ) , TTLSec : int ( ep . RecordTTL ) , } , } ) } } } // Generate Updates for zoneID , updates := range updatesByZone { zone := zonesByID [ zoneID ] if len ( updates ) == 0 { log . WithFields ( log . Fields { " " : zoneID , " " : zone . Domain , } ) . Debug ( " " ) continue } records := recordsByZoneID [ zoneID ] for _ , ep := range updates { matchedRecords := getRecordID ( records , zone , ep ) if len ( matchedRecords ) == 0 { log . WithFields ( log . Fields { " " : zoneID , " " : ep . DNSName , " " : zone . Domain , " " : ep . RecordType , } ) . Warn ( " " ) } recordType , err := convertRecordType ( ep . RecordType ) if err != nil { return err } matchedRecordsByTarget := make ( map [ string ] * linodego . DomainRecord ) for _ , record := range matchedRecords { matchedRecordsByTarget [ record . Target ] = record } for _ , target := range ep . Targets { if record , ok := matchedRecordsByTarget [ target ] ; ok { log . WithFields ( log . Fields { " " : zoneID , " " : ep . DNSName , " " : zone . Domain , " " : ep . RecordType , " " : target , } ) . Warn ( " " ) linodeUpdates = append ( linodeUpdates , & LinodeChangeUpdate { Domain : zone , DomainRecord : record , Options : linodego . DomainRecordUpdateOptions { Target : target , Name : getStrippedRecordName ( zone , ep ) , Type : recordType , Weight : getWeight ( ) , Port : getPort ( ) , Priority : getPriority ( ) , TTLSec : int ( ep . RecordTTL ) , } , } ) delete ( matchedRecordsByTarget , target ) } else { // Record did not previously exist, create new 'target' log . WithFields ( log . Fields { " " : zoneID , " " : ep . DNSName , " " : zone . Domain , " " : ep . RecordType , " " : target , } ) . Warn ( " " ) linodeCreates = append ( linodeCreates , & LinodeChangeCreate { Domain : zone , Options : linodego . DomainRecordCreateOptions { Target : target , Name : getStrippedRecordName ( zone , ep ) , Type : recordType , Weight : getWeight ( ) , Port : getPort ( ) , Priority : getPriority ( ) , TTLSec : int ( ep . RecordTTL ) , } , } ) } } // Any remaining records have been removed, delete them for _ , record := range matchedRecordsByTarget { log . WithFields ( log . Fields { " " : zoneID , " " : ep . DNSName , " " : zone . Domain , " " : ep . RecordType , " " : record . Target , } ) . Warn ( " " ) linodeDeletes = append ( linodeDeletes , & LinodeChangeDelete { Domain : zone , DomainRecord : record , } ) } } } // Generate Deletes for zoneID , deletes := range deletesByZone { zone := zonesByID [ zoneID ] if len ( deletes ) == 0 { log . WithFields ( log . Fields { " " : zoneID , " " : zone . Domain , } ) . Debug ( " " ) continue } records := recordsByZoneID [ zoneID ] for _ , ep := range deletes { matchedRecords := getRecordID ( records , zone , ep ) if len ( matchedRecords ) == 0 { log . WithFields ( log . Fields { " " : zoneID , " " : ep . DNSName , " " : zone . Domain , " " : ep . RecordType , } ) . Warn ( " " ) } for _ , record := range matchedRecords { linodeDeletes = append ( linodeDeletes , & LinodeChangeDelete { Domain : zone , DomainRecord : record , } ) } } } return p . submitChanges ( LinodeChanges { Creates : linodeCreates , Deletes : linodeDeletes , Updates : linodeUpdates , } ) }
// ApplyChanges applies a given set of changes in a given zone.
ApplyChanges applies a given set of changes in a given zone .