func main() { numProcs := runtime.NumCPU() * 2 runtime.GOMAXPROCS(numProcs) numWorkers := numProcs * 4 c := getCfr() workCh := make(chan *cfr.Distribution) wg := sync.WaitGroup{} wg.Add(numWorkers) log.Debugf("Spawning %v workers", numWorkers) for i := 0; i < numWorkers; i++ { go work(c, workCh, &wg) } dists, err := cfr.ListDistributions(c) if err != nil { log.Fatalf("Error listing distributions: %v", err) return } for _, dist := range dists { workCh <- dist } // Signal end of work for i := 0; i < numWorkers; i++ { workCh <- nil } wg.Wait() log.Debug("cfrjanitor done.") }
// loadHosts loads the initial list of hosts based on the existing entries in // the CDN and DNS services we manage func loadHosts() (map[string]*host, error) { log.Debug("Loading existing CloudFlare records ...") cflRecs, err := cflutil.GetAllRecords() if err != nil { return nil, fmt.Errorf("Unable to load Cloudflare records: %v", err) } log.Debugf("Loaded %d existing Cloudflare records", len(cflRecs)) log.Debug("Loading existing DNSimple records ...") dspRecs, err := dsputil.GetAllRecords() if err != nil { return nil, fmt.Errorf("Unable to load DNSimple records: %v", err) } log.Debugf("Loaded %d existing DNSimple records", len(dspRecs)) dists, err := cfr.ListDistributions(cfrutil) if err != nil { return nil, fmt.Errorf("Unable to load cloudfront distributions: %v", err) } log.Debugf("Loaded %d existing distributions", len(dists)) // Collect round-robin entries in Cloudflare cflGroups := make(map[string]map[string]*cloudflare.Record, 0) addToCflGroup := func(name string, r cloudflare.Record) { log.Debugf("Adding to %v: %v", name, r.Value) g := cflGroups[name] if g == nil { g = make(map[string]*cloudflare.Record, 1) cflGroups[name] = g } g[r.Value] = &r } // Collect round-robin entries in DNSimple dspGroups := make(map[string]map[string]*dnsimple.Record, 0) addToDspGroup := func(name string, r dnsimple.Record) { log.Debugf("Adding to %v: %v", name, r.Content) g := dspGroups[name] if g == nil { g = make(map[string]*dnsimple.Record, 1) dspGroups[name] = g } g[r.Content] = &r } // Build map of existing hosts preHosts := make(map[string]*host) addHost := func(name string, ip string, cflRec *cloudflare.Record, dspRec *dnsimple.Record) { h := preHosts[ip] if h == nil { h = &host{name: name, ip: ip} preHosts[ip] = h } if cflRec != nil { h.cflRecord = cflRec } if dspRec != nil { h.dspRecord = dspRec } } // Look through Cloudflare records to find peers, fallbacks and groups for _, r := range cflRecs { if isFallback(r.Name) { log.Debugf("Adding fallback: %v", r.Name) addHost(r.Name, r.Value, &r, nil) } else if isPeer(r.Name) { log.Debugf("Not adding peer: %v", r.Name) } else if r.Name == RoundRobin { addToCflGroup(RoundRobin, r) } else if r.Name == Fallbacks { addToCflGroup(Fallbacks, r) } else if r.Name == Peers { addToCflGroup(Peers, r) } else if strings.HasSuffix(r.Name, ".fallbacks") { addToCflGroup(r.Name, r) } else { log.Tracef("Unrecognized Cloudflare record: %v", r.FullName) } } // Look through DNSimple records to find peers, fallbacks and groups for _, r := range dspRecs { if isFallback(r.Name) { log.Debugf("Adding fallback: %v", r.Name) addHost(r.Name, r.Content, nil, &r) } else if isPeer(r.Name) { log.Debugf("Not adding peer: %v", r.Name) } else if r.Name == RoundRobin { addToDspGroup(RoundRobin, r) } else if r.Name == Fallbacks { addToDspGroup(Fallbacks, r) } else if r.Name == Peers { addToDspGroup(Peers, r) } else if strings.HasSuffix(r.Name, ".fallbacks") { addToDspGroup(r.Name, r) } else { log.Tracef("Unrecognized DNSimple record: %v", r.Name) } } hostsByName := make(map[string]*host) hostsByIp := make(map[string]*host) for _, pre := range preHosts { h := newHost(pre.name, pre.ip, "", pre.cflRecord, pre.dspRecord) hostsByName[h.name] = h hostsByIp[h.ip] = h } for _, d := range dists { h, found := hostsByName[d.InstanceId] if found { h.cfrDist = d } } // Update hosts with Cloudflare group info for _, h := range hostsByIp { for _, hg := range h.cflGroups { g, found := cflGroups[hg.subdomain] if found { hg.existing = g[h.ip] delete(g, h.ip) } } // Don't accept round robins unless we have a working Cloudfront // distribution if h.cfrDistReady() { for _, hg := range h.dspGroups { g, found := dspGroups[hg.subdomain] if found { hg.existing = g[h.ip] delete(g, h.ip) } } } } var wg sync.WaitGroup // Remove items from rotation that don't have a corresponding host for k, g := range cflGroups { for _, r := range g { wg.Add(1) go removeCflRecord(&wg, k, r) } } for k, g := range dspGroups { for _, r := range g { wg.Add(1) go removeDspRecord(&wg, k, r) } } wg.Wait() // Start hosts for _, h := range hostsByIp { go h.run() } return hostsByIp, nil }