Raw
  1package obs
  2
  3import (
  4	"errors"
  5	"sync"
  6	"time"
  7
  8	"github.com/prometheus/client_golang/prometheus"
  9)
 10
 11var errCommit = errors.New("obs: a commit is 40 or 64 lowercase hexadecimal digits")
 12
 13var (
 14	commitInfoDesc = prometheus.NewDesc("tug_config_commit_info",
 15		"The configuration in force: its source, and for a git source the applied commit.", []string{"source", "commit"}, nil)
 16	lastFetchDesc = prometheus.NewDesc("tug_config_last_fetch_success_timestamp_seconds",
 17		"When the git source last fetched its remote successfully, in Unix seconds.", nil, nil)
 18	rollbackDesc = prometheus.NewDesc("tug_config_rollback_refused_total",
 19		"Fetched commits refused because the applied commit is not their ancestor.", nil, nil)
 20)
 21
 22type ConfigSource struct {
 23	mu      sync.Mutex
 24	source  string
 25	commit  string
 26	fetched time.Time
 27	refused uint64
 28}
 29
 30func (r *Registry) ConfigSource(source string) *ConfigSource {
 31	if r == nil {
 32		return nil
 33	}
 34	if source != "file" && source != "git" {
 35		source = "unknown"
 36	}
 37	c := &ConfigSource{source: source}
 38	r.reg.MustRegister(c)
 39	return c
 40}
 41
 42func (c *ConfigSource) Applied(commit string) error {
 43	if c == nil {
 44		return nil
 45	}
 46	if !validCommit(commit) {
 47		return errCommit
 48	}
 49	c.mu.Lock()
 50	defer c.mu.Unlock()
 51	c.commit = commit
 52	return nil
 53}
 54
 55func validCommit(s string) bool {
 56	if len(s) != 40 && len(s) != 64 {
 57		return false
 58	}
 59	for i := 0; i < len(s); i++ {
 60		if !('0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f') {
 61			return false
 62		}
 63	}
 64	return true
 65}
 66
 67func (c *ConfigSource) Fetched(at time.Time) {
 68	if c == nil {
 69		return
 70	}
 71	c.mu.Lock()
 72	defer c.mu.Unlock()
 73	c.fetched = at
 74}
 75
 76func (c *ConfigSource) RollbackRefused() {
 77	if c == nil {
 78		return
 79	}
 80	c.mu.Lock()
 81	defer c.mu.Unlock()
 82	c.refused++
 83}
 84
 85func (c *ConfigSource) Describe(ch chan<- *prometheus.Desc) {
 86	ch <- commitInfoDesc
 87	ch <- lastFetchDesc
 88	ch <- rollbackDesc
 89}
 90
 91func (c *ConfigSource) Collect(ch chan<- prometheus.Metric) {
 92	c.mu.Lock()
 93	source, commit, fetched, refused := c.source, c.commit, c.fetched, c.refused
 94	c.mu.Unlock()
 95	ch <- prometheus.MustNewConstMetric(commitInfoDesc, prometheus.GaugeValue, 1, source, commit)
 96	if !fetched.IsZero() {
 97		ch <- prometheus.MustNewConstMetric(lastFetchDesc, prometheus.GaugeValue, float64(fetched.UnixNano())/1e9)
 98	}
 99	ch <- prometheus.MustNewConstMetric(rollbackDesc, prometheus.CounterValue, float64(refused))
100}