package obs import ( "errors" "sync" "time" "github.com/prometheus/client_golang/prometheus" ) var errCommit = errors.New("obs: a commit is 40 or 64 lowercase hexadecimal digits") var ( commitInfoDesc = prometheus.NewDesc("tug_config_commit_info", "The configuration in force: its source, and for a git source the applied commit.", []string{"source", "commit"}, nil) lastFetchDesc = prometheus.NewDesc("tug_config_last_fetch_success_timestamp_seconds", "When the git source last fetched its remote successfully, in Unix seconds.", nil, nil) rollbackDesc = prometheus.NewDesc("tug_config_rollback_refused_total", "Fetched commits refused because the applied commit is not their ancestor.", nil, nil) ) type ConfigSource struct { mu sync.Mutex source string commit string fetched time.Time refused uint64 } func (r *Registry) ConfigSource(source string) *ConfigSource { if r == nil { return nil } if source != "file" && source != "git" { source = "unknown" } c := &ConfigSource{source: source} r.reg.MustRegister(c) return c } func (c *ConfigSource) Applied(commit string) error { if c == nil { return nil } if !validCommit(commit) { return errCommit } c.mu.Lock() defer c.mu.Unlock() c.commit = commit return nil } func validCommit(s string) bool { if len(s) != 40 && len(s) != 64 { return false } for i := 0; i < len(s); i++ { if !('0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f') { return false } } return true } func (c *ConfigSource) Fetched(at time.Time) { if c == nil { return } c.mu.Lock() defer c.mu.Unlock() c.fetched = at } func (c *ConfigSource) RollbackRefused() { if c == nil { return } c.mu.Lock() defer c.mu.Unlock() c.refused++ } func (c *ConfigSource) Describe(ch chan<- *prometheus.Desc) { ch <- commitInfoDesc ch <- lastFetchDesc ch <- rollbackDesc } func (c *ConfigSource) Collect(ch chan<- prometheus.Metric) { c.mu.Lock() source, commit, fetched, refused := c.source, c.commit, c.fetched, c.refused c.mu.Unlock() ch <- prometheus.MustNewConstMetric(commitInfoDesc, prometheus.GaugeValue, 1, source, commit) if !fetched.IsZero() { ch <- prometheus.MustNewConstMetric(lastFetchDesc, prometheus.GaugeValue, float64(fetched.UnixNano())/1e9) } ch <- prometheus.MustNewConstMetric(rollbackDesc, prometheus.CounterValue, float64(refused)) }