// UpdateDeps is used to update the values of the dependencies for a template func (d *DedupManager) UpdateDeps(t *template.Template, deps []dep.Dependency) error { // Calculate the path to write updates to dataPath := path.Join(*d.config.Prefix, t.ID(), "data") // Package up the dependency data td := templateData{ Data: make(map[string]interface{}), } for _, dp := range deps { // Skip any dependencies that can't be shared if !dp.CanShare() { continue } // Pull the current value from the brain val, ok := d.brain.Recall(dp) if ok { td.Data[dp.String()] = val } } // Encode via GOB and LZW compress var buf bytes.Buffer compress := lzw.NewWriter(&buf, lzw.LSB, 8) enc := gob.NewEncoder(compress) if err := enc.Encode(&td); err != nil { return fmt.Errorf("encode failed: %v", err) } compress.Close() // Compute MD5 of the buffer hash := md5.Sum(buf.Bytes()) d.lastWriteLock.RLock() existing, ok := d.lastWrite[t] d.lastWriteLock.RUnlock() if ok && bytes.Equal(existing, hash[:]) { log.Printf("[INFO] (dedup) de-duplicate data '%s' already current", dataPath) return nil } // Write the KV update kvPair := consulapi.KVPair{ Key: dataPath, Value: buf.Bytes(), Flags: templateDataFlag, } client := d.clients.Consul() if _, err := client.KV().Put(&kvPair, nil); err != nil { return fmt.Errorf("failed to write '%s': %v", dataPath, err) } log.Printf("[INFO] (dedup) updated de-duplicate data '%s'", dataPath) d.lastWriteLock.Lock() d.lastWrite[t] = hash[:] d.lastWriteLock.Unlock() return nil }
func (d *DedupManager) attemptLock(client *consulapi.Client, session string, sessionCh chan struct{}, t *template.Template) { defer d.wg.Done() START: log.Printf("[INFO] (dedup) attempting lock for template hash %s", t.ID()) basePath := path.Join(*d.config.Prefix, t.ID()) lopts := &consulapi.LockOptions{ Key: path.Join(basePath, "lock"), Session: session, MonitorRetries: 3, MonitorRetryTime: 3 * time.Second, } lock, err := client.LockOpts(lopts) if err != nil { log.Printf("[ERR] (dedup) failed to create lock '%s': %v", lopts.Key, err) return } var retryCh <-chan time.Time leaderCh, err := lock.Lock(sessionCh) if err != nil { log.Printf("[ERR] (dedup) failed to acquire lock '%s': %v", lopts.Key, err) retryCh = time.After(lockRetry) } else { log.Printf("[INFO] (dedup) acquired lock '%s'", lopts.Key) d.setLeader(t, leaderCh) } select { case <-retryCh: retryCh = nil goto START case <-leaderCh: log.Printf("[WARN] (dedup) lost lock ownership '%s'", lopts.Key) d.setLeader(t, nil) goto START case <-sessionCh: log.Printf("[INFO] (dedup) releasing lock '%s'", lopts.Key) d.setLeader(t, nil) lock.Unlock() case <-d.stopCh: log.Printf("[INFO] (dedup) releasing lock '%s'", lopts.Key) lock.Unlock() } }
// TemplateConfigFor returns the TemplateConfig for the given Template func (r *Runner) templateConfigsFor(tmpl *template.Template) []*config.TemplateConfig { return r.ctemplatesMap[tmpl.ID()] }
// ConfigTemplateFor returns the ConfigTemplate for the given Template func (r *Runner) configTemplatesFor(tmpl *template.Template) []*config.ConfigTemplate { return r.ctemplatesMap[tmpl.ID()] }
func (d *DedupManager) watchTemplate(client *consulapi.Client, t *template.Template) { log.Printf("[INFO] (dedup) starting watch for template hash %s", t.ID()) path := path.Join(*d.config.Prefix, t.ID(), "data") // Determine if stale queries are allowed var allowStale bool if *d.config.MaxStale != 0 { allowStale = true } // Setup our query options opts := &consulapi.QueryOptions{ AllowStale: allowStale, WaitTime: 60 * time.Second, } START: // Stop listening if we're stopped select { case <-d.stopCh: return default: } // If we are current the leader, wait for leadership lost d.leaderLock.RLock() lockCh, ok := d.leader[t] d.leaderLock.RUnlock() if ok { select { case <-lockCh: goto START case <-d.stopCh: return } } // Block for updates on the data key log.Printf("[INFO] (dedup) listing data for template hash %s", t.ID()) pair, meta, err := client.KV().Get(path, opts) if err != nil { log.Printf("[ERR] (dedup) failed to get '%s': %v", path, err) select { case <-time.After(listRetry): goto START case <-d.stopCh: return } } opts.WaitIndex = meta.LastIndex // If we've exceeded the maximum staleness, retry without stale if allowStale && meta.LastContact > *d.config.MaxStale { allowStale = false log.Printf("[DEBUG] (dedup) %s stale data (last contact exceeded max_stale)", path) goto START } // Re-enable stale queries if allowed if *d.config.MaxStale > 0 { allowStale = true } // Stop listening if we're stopped select { case <-d.stopCh: return default: } // If we are current the leader, wait for leadership lost d.leaderLock.RLock() lockCh, ok = d.leader[t] d.leaderLock.RUnlock() if ok { select { case <-lockCh: goto START case <-d.stopCh: return } } // Parse the data file if pair != nil && pair.Flags == templateDataFlag { d.parseData(pair.Key, pair.Value) } goto START }