Skip to content

Commit b1f033b

Browse files
authored
Merge pull request #2 from cernops/feat/extra_metrics
Feat/extra metrics
2 parents 0b400af + 0e3956f commit b1f033b

11 files changed

Lines changed: 1375 additions & 30 deletions

puppetreport/collector.go

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,48 @@ var (
4444
nil,
4545
nil,
4646
)
47+
48+
resourcesTotalDesc = prometheus.NewDesc(
49+
"puppet_last_catalog_resources_total",
50+
"Resources managed during the last Puppet run",
51+
nil,
52+
nil,
53+
)
54+
55+
resourcesStateDesc = prometheus.NewDesc(
56+
"puppet_last_catalog_resources",
57+
"Resource states encountered during the last Puppet run",
58+
[]string{"state"},
59+
nil,
60+
)
61+
62+
changesTotalDesc = prometheus.NewDesc(
63+
"puppet_last_catalog_changes_total",
64+
"Applied node changes during the last Puppet run",
65+
nil,
66+
nil,
67+
)
68+
69+
eventsTotalDesc = prometheus.NewDesc(
70+
"puppet_last_catalog_events_total",
71+
"Events fired during the last Puppet run",
72+
nil,
73+
nil,
74+
)
75+
76+
eventsStateDesc = prometheus.NewDesc(
77+
"puppet_last_catalog_events",
78+
"Events states encountered during the last Puppet run",
79+
[]string{"state"},
80+
nil,
81+
)
82+
83+
configRetrievalDurationDesc = prometheus.NewDesc(
84+
"puppet_config_retrieval_duration_seconds",
85+
"Duration of the config retrieval stage.",
86+
nil,
87+
nil,
88+
)
4789
)
4890

4991
type Collector struct {
@@ -56,6 +98,11 @@ func (c Collector) Describe(ch chan<- *prometheus.Desc) {
5698
ch <- runAtDesc
5799
ch <- runDurationDesc
58100
ch <- runSuccessDesc
101+
ch <- resourcesTotalDesc
102+
ch <- resourcesStateDesc
103+
ch <- changesTotalDesc
104+
ch <- eventsTotalDesc
105+
ch <- configRetrievalDurationDesc
59106
}
60107

61108
func (c Collector) Collect(ch chan<- prometheus.Metric) {
@@ -80,15 +127,33 @@ type Logger interface {
80127
}
81128

82129
type interpretedReport struct {
83-
RunAt float64
84-
RunDuration float64
85-
CatalogVersion string
86-
RunSuccess float64
130+
RunAt float64
131+
RunDuration float64
132+
CatalogVersion string
133+
RunSuccess float64
134+
ResourceCount float64
135+
ChangeCount float64
136+
EventCount float64
137+
ResourceStates map[string]float64
138+
EventStates map[string]float64
139+
ConfigRetrievalDuration float64
87140
}
88141

89142
func (r interpretedReport) collect(ch chan<- prometheus.Metric) {
90143
ch <- prometheus.MustNewConstMetric(catalogVersionDesc, prometheus.GaugeValue, 1, r.CatalogVersion)
91144
ch <- prometheus.MustNewConstMetric(runAtDesc, prometheus.GaugeValue, r.RunAt)
92145
ch <- prometheus.MustNewConstMetric(runDurationDesc, prometheus.GaugeValue, r.RunDuration)
93146
ch <- prometheus.MustNewConstMetric(runSuccessDesc, prometheus.GaugeValue, r.RunSuccess)
147+
ch <- prometheus.MustNewConstMetric(resourcesTotalDesc, prometheus.GaugeValue, r.ResourceCount)
148+
ch <- prometheus.MustNewConstMetric(changesTotalDesc, prometheus.GaugeValue, r.ChangeCount)
149+
ch <- prometheus.MustNewConstMetric(eventsTotalDesc, prometheus.GaugeValue, r.EventCount)
150+
ch <- prometheus.MustNewConstMetric(configRetrievalDurationDesc, prometheus.GaugeValue, r.ConfigRetrievalDuration)
151+
152+
for state, count := range r.ResourceStates {
153+
ch <- prometheus.MustNewConstMetric(resourcesStateDesc, prometheus.GaugeValue, count, state)
154+
}
155+
156+
for state, count := range r.EventStates {
157+
ch <- prometheus.MustNewConstMetric(eventsStateDesc, prometheus.GaugeValue, count, state)
158+
}
94159
}

puppetreport/report.go

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,31 +35,82 @@ type runReport struct {
3535

3636
func (r runReport) interpret() interpretedReport {
3737
result := interpretedReport{
38-
RunAt: asUnixSeconds(r.Time),
39-
RunDuration: r.totalDuration(),
40-
CatalogVersion: r.ConfigurationVersion,
38+
RunAt: asUnixSeconds(r.Time),
39+
RunDuration: -1,
40+
CatalogVersion: r.ConfigurationVersion,
41+
ConfigRetrievalDuration: -1,
4142
}
4243
if r.success() {
4344
result.RunSuccess = 1
4445
}
46+
47+
resourceMetrics, ok := r.Metrics["resources"]
48+
if ok {
49+
interpretResourceMetrics(resourceMetrics.Values(), &result)
50+
}
51+
52+
timeMetrics, ok := r.Metrics["time"]
53+
if ok {
54+
interpretTimeMetrics(timeMetrics.Values(), &result)
55+
}
56+
57+
changeMetrics, ok := r.Metrics["changes"]
58+
if ok {
59+
interpretChangeMetrics(changeMetrics.Values(), &result)
60+
}
61+
62+
eventMetrics, ok := r.Metrics["events"]
63+
if ok {
64+
interpretEventMetrics(eventMetrics.Values(), &result)
65+
}
66+
4567
return result
4668
}
4769

48-
func asUnixSeconds(t time.Time) float64 {
49-
return float64(t.Unix()) + (float64(t.Nanosecond()) / 1e+9)
70+
func interpretResourceMetrics(m map[string]float64, r *interpretedReport) {
71+
r.ResourceStates = make(map[string]float64, len(m))
72+
73+
for l, v := range m {
74+
if l == "total" {
75+
r.ResourceCount = v
76+
} else {
77+
r.ResourceStates[l] = v
78+
}
79+
}
5080
}
5181

52-
func (r runReport) totalDuration() float64 {
53-
timeMetrics, ok := r.Metrics["time"]
54-
if !ok {
55-
return -1
82+
func interpretTimeMetrics(m map[string]float64, r *interpretedReport) {
83+
total, ok := m["total"]
84+
if ok {
85+
r.RunDuration = total
5686
}
57-
values := timeMetrics.Values()
58-
total, ok := values["total"]
59-
if !ok {
60-
return -1
87+
config_retrieval, ok := m["config_retrieval"]
88+
if ok {
89+
r.ConfigRetrievalDuration = config_retrieval
90+
}
91+
}
92+
93+
func interpretChangeMetrics(m map[string]float64, r *interpretedReport) {
94+
total, ok := m["total"]
95+
if ok {
96+
r.ChangeCount = total
97+
}
98+
}
99+
100+
func interpretEventMetrics(m map[string]float64, r *interpretedReport) {
101+
r.EventStates = make(map[string]float64, len(m))
102+
103+
for l, v := range m {
104+
if l == "total" {
105+
r.EventCount = v
106+
} else {
107+
r.EventStates[l] = v
108+
}
61109
}
62-
return total
110+
}
111+
112+
func asUnixSeconds(t time.Time) float64 {
113+
return float64(t.Unix()) + (float64(t.Nanosecond()) / 1e+9)
63114
}
64115

65116
func (r runReport) success() bool {

puppetreport/report_test.go

Lines changed: 159 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,169 @@
1414

1515
package puppetreport
1616

17-
import "testing"
17+
import (
18+
"fmt"
19+
"testing"
20+
)
1821

1922
func TestLoadReport(t *testing.T) {
20-
report, err := load("last_run_report.yaml")
21-
if err != nil {
22-
t.Fatal(err)
23+
testCases := map[string]interpretedReport{
24+
"last_run_report": interpretedReport{
25+
RunAt: 1618957125.5901103,
26+
RunDuration: 17.199882286,
27+
CatalogVersion: "1618957129",
28+
RunSuccess: 1,
29+
},
30+
"last_run_report-5.4.0": interpretedReport{
31+
RunAt: 1725776230.602652,
32+
RunDuration: 0.03196727,
33+
CatalogVersion: "1725776230",
34+
RunSuccess: 1,
35+
ResourceCount: 8,
36+
ResourceStates: map[string]float64{
37+
"skipped": 0,
38+
"failed": 0,
39+
"failed_to_restart": 0,
40+
"restarted": 0,
41+
"changed": 0,
42+
"out_of_sync": 0,
43+
"scheduled": 0,
44+
"corrective_change": 0,
45+
},
46+
EventStates: map[string]float64{
47+
"failure": 0,
48+
"success": 0,
49+
},
50+
},
51+
"last_run_report-6.28.0": interpretedReport{
52+
RunAt: 1725776354.854867,
53+
RunDuration: 0.004820335,
54+
CatalogVersion: "1725776354",
55+
RunSuccess: 1,
56+
ResourceCount: 8,
57+
ResourceStates: map[string]float64{
58+
"skipped": 0,
59+
"failed": 0,
60+
"failed_to_restart": 0,
61+
"restarted": 0,
62+
"changed": 0,
63+
"out_of_sync": 0,
64+
"scheduled": 0,
65+
"corrective_change": 0,
66+
},
67+
EventStates: map[string]float64{
68+
"failure": 0,
69+
"success": 0,
70+
},
71+
},
72+
"last_run_report-7.32.1": interpretedReport{
73+
RunAt: 1725776438.356112,
74+
RunDuration: 0.006013873,
75+
CatalogVersion: "1725776438",
76+
RunSuccess: 1,
77+
ResourceCount: 8,
78+
ResourceStates: map[string]float64{
79+
"skipped": 0,
80+
"failed": 0,
81+
"failed_to_restart": 0,
82+
"restarted": 0,
83+
"changed": 0,
84+
"out_of_sync": 0,
85+
"scheduled": 0,
86+
"corrective_change": 0,
87+
},
88+
EventStates: map[string]float64{
89+
"failure": 0,
90+
"success": 0,
91+
},
92+
},
93+
"last_run_report-8.8.1": interpretedReport{
94+
RunAt: 1725776515.039312,
95+
RunDuration: 0.005837204,
96+
CatalogVersion: "1725776515",
97+
RunSuccess: 1,
98+
ResourceCount: 8,
99+
ResourceStates: map[string]float64{
100+
"skipped": 0,
101+
"failed": 0,
102+
"failed_to_restart": 0,
103+
"restarted": 0,
104+
"changed": 0,
105+
"out_of_sync": 0,
106+
"scheduled": 0,
107+
"corrective_change": 0,
108+
},
109+
EventStates: map[string]float64{
110+
"failure": 0,
111+
"success": 0,
112+
},
113+
},
23114
}
24115

25-
ir := report.interpret()
26-
expected := interpretedReport{
27-
RunAt: 1618957125.5901103,
28-
RunDuration: 17.199882286,
29-
CatalogVersion: "1618957129",
30-
RunSuccess: 1,
116+
for name, tc := range testCases {
117+
want := tc
118+
t.Run(name, func(t *testing.T) {
119+
report, err := load("testdata/" + name + ".yaml")
120+
if err != nil {
121+
t.Fatal(err)
122+
}
123+
124+
got := report.interpret()
125+
126+
if want.RunAt != got.RunAt {
127+
t.Fatalf("RunAt: want %f; got %f", want.RunAt, got.RunAt)
128+
}
129+
130+
if want.RunDuration != got.RunDuration {
131+
t.Fatalf("RunDuration: want %f; got %f", want.RunDuration, got.RunDuration)
132+
}
133+
134+
if want.CatalogVersion != got.CatalogVersion {
135+
t.Fatalf("CatalogVersion: want %q; got %q", want.CatalogVersion, got.CatalogVersion)
136+
}
137+
138+
if want.RunSuccess != got.RunSuccess {
139+
t.Fatalf("RunSuccess: want %f; got %f", want.RunSuccess, got.RunSuccess)
140+
}
141+
142+
if want.ResourceCount != got.ResourceCount {
143+
t.Fatalf("ResourceCount: want %f; got %f", want.ResourceCount, got.ResourceCount)
144+
}
145+
146+
if want.ChangeCount != got.ChangeCount {
147+
t.Fatalf("ChangeCount: want %f; got %f", want.ChangeCount, got.ChangeCount)
148+
}
149+
150+
if want.EventCount != got.EventCount {
151+
t.Fatalf("EventCount: want %f; got %f", want.EventCount, got.EventCount)
152+
}
153+
154+
if err := mapCompare(want.ResourceStates, got.ResourceStates); err != "" {
155+
t.Fatalf("ResourceStates: %s", err)
156+
}
157+
158+
if err := mapCompare(want.EventStates, got.EventStates); err != "" {
159+
t.Fatalf("EventStates: %s", err)
160+
}
161+
})
162+
}
163+
}
164+
165+
func mapCompare(l, r map[string]float64) string {
166+
if len(l) != len(r) {
167+
return fmt.Sprintf("length: want %d, got %d", len(l), len(r))
31168
}
32-
if ir != expected {
33-
t.Fatalf("%+v != %+v", ir, expected)
169+
170+
for k, want := range l {
171+
got, ok := r[k]
172+
if !ok {
173+
return fmt.Sprintf("key %q is missing", k)
174+
}
175+
176+
if want != got {
177+
return fmt.Sprintf("key %q: want %f, got %f", k, want, got)
178+
}
34179
}
180+
181+
return ""
35182
}

puppetreport/testdata/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.deb

0 commit comments

Comments
 (0)