Beispiel #1
0
// 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
}
Beispiel #2
0
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()
	}
}
Beispiel #3
0
// TemplateConfigFor returns the TemplateConfig for the given Template
func (r *Runner) templateConfigsFor(tmpl *template.Template) []*config.TemplateConfig {
	return r.ctemplatesMap[tmpl.ID()]
}
Beispiel #4
0
// ConfigTemplateFor returns the ConfigTemplate for the given Template
func (r *Runner) configTemplatesFor(tmpl *template.Template) []*config.ConfigTemplate {
	return r.ctemplatesMap[tmpl.ID()]
}
Beispiel #5
0
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
}