/
version.go
132 lines (121 loc) · 2.94 KB
/
version.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package collector
import (
"log"
"sync"
"time"
"github.com/Masterminds/semver"
"github.com/caarlos0/version_exporter/client"
"github.com/caarlos0/version_exporter/config"
"github.com/prometheus/client_golang/prometheus"
)
type versionCollector struct {
mutex sync.Mutex
config *config.Config
client client.Client
up *prometheus.Desc
upToDate *prometheus.Desc
scrapeDuration *prometheus.Desc
}
// NewVersionCollector returns a versions collector
func NewVersionCollector(config *config.Config, client client.Client) prometheus.Collector {
const namespace = "version"
const subsystem = ""
return &versionCollector{
config: config,
client: client,
up: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "up"),
"Exporter is being able to talk with GitHub API",
nil,
nil,
),
upToDate: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "up_to_date"),
"Wether the repository latest version is in the specified semantic versioning range",
[]string{"repository", "constraint", "latest"},
nil,
),
scrapeDuration: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "scrape_duration_seconds"),
"Returns how long the probe took to complete in seconds",
nil,
nil,
),
}
}
// Describe all metrics
func (c *versionCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.up
ch <- c.upToDate
ch <- c.scrapeDuration
}
// Collect all metrics
func (c *versionCollector) Collect(ch chan<- prometheus.Metric) {
c.mutex.Lock()
defer c.mutex.Unlock()
success := true
start := time.Now()
for repo, constraint := range c.config.Repositories {
sconstraint, err := semver.NewConstraint(constraint)
if err != nil {
log.Printf("failed to collect for %s: %s", repo, err.Error())
success = false
continue
}
version, err := getLatest(c.client, repo)
if err != nil {
log.Printf("failed to collect for %s: %s", repo, err.Error())
success = false
continue
}
if version == nil {
continue
}
up := sconstraint.Check(version)
ch <- prometheus.MustNewConstMetric(
c.upToDate,
prometheus.GaugeValue,
boolToFloat(up),
repo,
constraint,
version.String(),
)
}
ch <- prometheus.MustNewConstMetric(
c.up,
prometheus.GaugeValue,
boolToFloat(success),
)
ch <- prometheus.MustNewConstMetric(
c.scrapeDuration,
prometheus.GaugeValue,
time.Since(start).Seconds(),
)
}
func getLatest(client client.Client, repo string) (*semver.Version, error) {
releases, err := client.Releases(repo)
if err != nil {
return nil, err
}
for _, release := range releases {
if release.Draft || release.Prerelease {
continue
}
version, err := semver.NewVersion(release.TagName)
if err != nil {
log.Printf("failed to parse tag %s: %s", release.TagName, err)
continue
}
if version.Prerelease() != "" {
continue
}
return version, nil
}
return nil, nil
}
func boolToFloat(b bool) float64 {
if b {
return 1.0
}
return 0.0
}