func (s *Syncer) Run(stopCh <-chan struct{}) error {
	for _, d := range s.domains {
		go s.poll(d, stopCh)
		go s.sync(d, stopCh)
	}
	<-stopCh
	return nil
}
Run() starts the sync and poll loops
package sync
import (
	"context"
	"fmt"
	"log"
	"sync"
	"time"
	"github.com/ianlewis/cloud-dyndns-client/pkg/backend"
)
type Record struct {
	Record  backend.DNSRecord
	Backend backend.DNSBackend
}
type domainObj struct {
	remote backend.DNSRecord
	managed backend.DNSRecord
	backend backend.DNSBackend
	lock sync.Mutex
	needSync chan struct{}
	initialized bool
}
type Syncer struct {
	domains []*domainObj
	pollInterval time.Duration
	syncReconcileInterval time.Duration
	apiTimeout time.Duration
}
func NewSyncer(records []Record, pollInterval, apiTimeout time.Duration) *Syncer {
	o := []*domainObj{}
	for _, r := range records {
		o = append(o, &domainObj{
			remote:      r.Record,
			managed:     r.Record,
			backend:     r.Backend,
			needSync:    make(chan struct{}, 1),
			initialized: false,
		})
	}
	return &Syncer{
		domains:               o,
		pollInterval:          pollInterval,
		syncReconcileInterval: 1 * time.Minute,
		apiTimeout:            apiTimeout,
	}
}
func (s *Syncer) UpdateRecord(dnsName, dnsType string, ttl int64, data []string) error {
	for _, d := range s.domains {
		if d.managed.Name() == dnsName && d.managed.Type() == dnsType {
			d.lock.Lock()
			d.managed = backend.NewDNSRecord(
				dnsName,
				dnsType,
				ttl,
				data,
			)
			d.lock.Unlock()
			d.needSync <- struct{}{}
			return nil
		}
	}
	return fmt.Errorf("Domain %s not registered.", dnsName)
}
func needsUpdate(l, r backend.DNSRecord) bool {
	if l == nil {
		return false
	}
	if r == nil {
		return true
	}
	if l.Name() != r.Name() || l.Type() != r.Type() {
		return false
	}
	if l.Ttl() != r.Ttl() {
		return true
	}
	lData := l.Data()
	rData := r.Data()
	if len(lData) <= 0 {
		return false
	}
	if len(lData) != len(rData) {
		return true
	}
	for i, d := range lData {
		if d != rData[i] {
			return true
		}
	}
	return false
}
func (s *Syncer) pollSingle(d *domainObj) error {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, s.apiTimeout)
	defer cancel()
	record, err := d.backend.GetRecord(ctx, d.managed.Name(), d.managed.Type())
	if err != nil {
		return err
	}
	d.lock.Lock()
	d.remote = record
	d.initialized = true
	d.needSync <- struct{}{}
	d.lock.Unlock()
	return nil
}
func (s *Syncer) poll(d *domainObj, stopCh <-chan struct{}) {
	if err := s.pollSingle(d); err != nil {
		log.Printf("Error polling DNS record %q: %v", d.managed.Name(), err)
	}
	for {
		select {
		case <-time.After(s.pollInterval):
			if err := s.pollSingle(d); err != nil {
				log.Printf("Error polling DNS record %q: %v", d.managed.Name(), err)
			}
		case <-stopCh:
			return
		}
	}
}
func (s *Syncer) syncSingle(d *domainObj) error {
	d.lock.Lock()
	defer d.lock.Unlock()
	if !needsUpdate(d.managed, d.remote) {
		return nil
	}
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, s.apiTimeout)
	defer cancel()
	additions := []backend.DNSRecord{d.managed}
	deletions := []backend.DNSRecord{}
	if d.remote != nil {
		deletions = []backend.DNSRecord{d.remote}
	}
	log.Printf("Updating record %v", d.managed.Name())
	err := d.backend.UpdateRecords(ctx, additions, deletions)
	if err != nil {
		return err
	}
	d.remote = d.managed
	return nil
}
func (s *Syncer) sync(d *domainObj, stopCh <-chan struct{}) {
	for {
		select {
		case <-d.needSync:
			if d.initialized {
				if err := s.syncSingle(d); err != nil {
					log.Printf("Error syncing DNS record %s %#v %#v", err.Error(), d, err)
				}
			}
		case <-time.After(s.syncReconcileInterval):
			if d.initialized {
				if err := s.syncSingle(d); err != nil {
					log.Printf("Error syncing DNS record %s %#v %#v", err.Error(), d, err)
				}
			}
		case <-stopCh:
			return
		}
	}
}