Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 69 additions & 4 deletions puppetreport/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,48 @@ var (
nil,
nil,
)

resourcesTotalDesc = prometheus.NewDesc(
"puppet_last_catalog_resources_total",
"Resources managed during the last Puppet run",
nil,
nil,
)

resourcesStateDesc = prometheus.NewDesc(
"puppet_last_catalog_resources",
"Resource states encountered during the last Puppet run",
[]string{"state"},
nil,
)

changesTotalDesc = prometheus.NewDesc(
"puppet_last_catalog_changes_total",
"Applied node changes during the last Puppet run",
nil,
nil,
)

eventsTotalDesc = prometheus.NewDesc(
"puppet_last_catalog_events_total",
"Events fired during the last Puppet run",
nil,
nil,
)

eventsStateDesc = prometheus.NewDesc(
"puppet_last_catalog_events",
"Events states encountered during the last Puppet run",
[]string{"state"},
nil,
)

configRetrievalDurationDesc = prometheus.NewDesc(
"puppet_config_retrieval_duration_seconds",
"Duration of the config retrieval stage.",
nil,
nil,
)
)

type Collector struct {
Expand All @@ -56,6 +98,11 @@ func (c Collector) Describe(ch chan<- *prometheus.Desc) {
ch <- runAtDesc
ch <- runDurationDesc
ch <- runSuccessDesc
ch <- resourcesTotalDesc
ch <- resourcesStateDesc
ch <- changesTotalDesc
ch <- eventsTotalDesc
ch <- configRetrievalDurationDesc
}

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

type interpretedReport struct {
RunAt float64
RunDuration float64
CatalogVersion string
RunSuccess float64
RunAt float64
RunDuration float64
CatalogVersion string
RunSuccess float64
ResourceCount float64
ChangeCount float64
EventCount float64
ResourceStates map[string]float64
EventStates map[string]float64
ConfigRetrievalDuration float64
}

func (r interpretedReport) collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(catalogVersionDesc, prometheus.GaugeValue, 1, r.CatalogVersion)
ch <- prometheus.MustNewConstMetric(runAtDesc, prometheus.GaugeValue, r.RunAt)
ch <- prometheus.MustNewConstMetric(runDurationDesc, prometheus.GaugeValue, r.RunDuration)
ch <- prometheus.MustNewConstMetric(runSuccessDesc, prometheus.GaugeValue, r.RunSuccess)
ch <- prometheus.MustNewConstMetric(resourcesTotalDesc, prometheus.GaugeValue, r.ResourceCount)
ch <- prometheus.MustNewConstMetric(changesTotalDesc, prometheus.GaugeValue, r.ChangeCount)
ch <- prometheus.MustNewConstMetric(eventsTotalDesc, prometheus.GaugeValue, r.EventCount)
ch <- prometheus.MustNewConstMetric(configRetrievalDurationDesc, prometheus.GaugeValue, r.ConfigRetrievalDuration)

for state, count := range r.ResourceStates {
ch <- prometheus.MustNewConstMetric(resourcesStateDesc, prometheus.GaugeValue, count, state)
}

for state, count := range r.EventStates {
ch <- prometheus.MustNewConstMetric(eventsStateDesc, prometheus.GaugeValue, count, state)
}
}
79 changes: 65 additions & 14 deletions puppetreport/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,82 @@ type runReport struct {

func (r runReport) interpret() interpretedReport {
result := interpretedReport{
RunAt: asUnixSeconds(r.Time),
RunDuration: r.totalDuration(),
CatalogVersion: r.ConfigurationVersion,
RunAt: asUnixSeconds(r.Time),
RunDuration: -1,
CatalogVersion: r.ConfigurationVersion,
ConfigRetrievalDuration: -1,
}
if r.success() {
result.RunSuccess = 1
}

resourceMetrics, ok := r.Metrics["resources"]
if ok {
interpretResourceMetrics(resourceMetrics.Values(), &result)
}

timeMetrics, ok := r.Metrics["time"]
if ok {
interpretTimeMetrics(timeMetrics.Values(), &result)
}

changeMetrics, ok := r.Metrics["changes"]
if ok {
interpretChangeMetrics(changeMetrics.Values(), &result)
}

eventMetrics, ok := r.Metrics["events"]
if ok {
interpretEventMetrics(eventMetrics.Values(), &result)
}

return result
}

func asUnixSeconds(t time.Time) float64 {
return float64(t.Unix()) + (float64(t.Nanosecond()) / 1e+9)
func interpretResourceMetrics(m map[string]float64, r *interpretedReport) {
r.ResourceStates = make(map[string]float64, len(m))

for l, v := range m {
if l == "total" {
r.ResourceCount = v
} else {
r.ResourceStates[l] = v
}
}
}

func (r runReport) totalDuration() float64 {
timeMetrics, ok := r.Metrics["time"]
if !ok {
return -1
func interpretTimeMetrics(m map[string]float64, r *interpretedReport) {
total, ok := m["total"]
if ok {
r.RunDuration = total
}
values := timeMetrics.Values()
total, ok := values["total"]
if !ok {
return -1
config_retrieval, ok := m["config_retrieval"]
if ok {
r.ConfigRetrievalDuration = config_retrieval
}
}

func interpretChangeMetrics(m map[string]float64, r *interpretedReport) {
total, ok := m["total"]
if ok {
r.ChangeCount = total
}
}

func interpretEventMetrics(m map[string]float64, r *interpretedReport) {
r.EventStates = make(map[string]float64, len(m))

for l, v := range m {
if l == "total" {
r.EventCount = v
} else {
r.EventStates[l] = v
}
}
return total
}

func asUnixSeconds(t time.Time) float64 {
return float64(t.Unix()) + (float64(t.Nanosecond()) / 1e+9)
}

func (r runReport) success() bool {
Expand Down
171 changes: 159 additions & 12 deletions puppetreport/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,169 @@

package puppetreport

import "testing"
import (
"fmt"
"testing"
)

func TestLoadReport(t *testing.T) {
report, err := load("last_run_report.yaml")
if err != nil {
t.Fatal(err)
testCases := map[string]interpretedReport{
"last_run_report": interpretedReport{
RunAt: 1618957125.5901103,
RunDuration: 17.199882286,
CatalogVersion: "1618957129",
RunSuccess: 1,
},
"last_run_report-5.4.0": interpretedReport{
RunAt: 1725776230.602652,
RunDuration: 0.03196727,
CatalogVersion: "1725776230",
RunSuccess: 1,
ResourceCount: 8,
ResourceStates: map[string]float64{
"skipped": 0,
"failed": 0,
"failed_to_restart": 0,
"restarted": 0,
"changed": 0,
"out_of_sync": 0,
"scheduled": 0,
"corrective_change": 0,
},
EventStates: map[string]float64{
"failure": 0,
"success": 0,
},
},
"last_run_report-6.28.0": interpretedReport{
RunAt: 1725776354.854867,
RunDuration: 0.004820335,
CatalogVersion: "1725776354",
RunSuccess: 1,
ResourceCount: 8,
ResourceStates: map[string]float64{
"skipped": 0,
"failed": 0,
"failed_to_restart": 0,
"restarted": 0,
"changed": 0,
"out_of_sync": 0,
"scheduled": 0,
"corrective_change": 0,
},
EventStates: map[string]float64{
"failure": 0,
"success": 0,
},
},
"last_run_report-7.32.1": interpretedReport{
RunAt: 1725776438.356112,
RunDuration: 0.006013873,
CatalogVersion: "1725776438",
RunSuccess: 1,
ResourceCount: 8,
ResourceStates: map[string]float64{
"skipped": 0,
"failed": 0,
"failed_to_restart": 0,
"restarted": 0,
"changed": 0,
"out_of_sync": 0,
"scheduled": 0,
"corrective_change": 0,
},
EventStates: map[string]float64{
"failure": 0,
"success": 0,
},
},
"last_run_report-8.8.1": interpretedReport{
RunAt: 1725776515.039312,
RunDuration: 0.005837204,
CatalogVersion: "1725776515",
RunSuccess: 1,
ResourceCount: 8,
ResourceStates: map[string]float64{
"skipped": 0,
"failed": 0,
"failed_to_restart": 0,
"restarted": 0,
"changed": 0,
"out_of_sync": 0,
"scheduled": 0,
"corrective_change": 0,
},
EventStates: map[string]float64{
"failure": 0,
"success": 0,
},
},
}

ir := report.interpret()
expected := interpretedReport{
RunAt: 1618957125.5901103,
RunDuration: 17.199882286,
CatalogVersion: "1618957129",
RunSuccess: 1,
for name, tc := range testCases {
want := tc
t.Run(name, func(t *testing.T) {
report, err := load("testdata/" + name + ".yaml")
if err != nil {
t.Fatal(err)
}

got := report.interpret()

if want.RunAt != got.RunAt {
t.Fatalf("RunAt: want %f; got %f", want.RunAt, got.RunAt)
}

if want.RunDuration != got.RunDuration {
t.Fatalf("RunDuration: want %f; got %f", want.RunDuration, got.RunDuration)
}

if want.CatalogVersion != got.CatalogVersion {
t.Fatalf("CatalogVersion: want %q; got %q", want.CatalogVersion, got.CatalogVersion)
}

if want.RunSuccess != got.RunSuccess {
t.Fatalf("RunSuccess: want %f; got %f", want.RunSuccess, got.RunSuccess)
}

if want.ResourceCount != got.ResourceCount {
t.Fatalf("ResourceCount: want %f; got %f", want.ResourceCount, got.ResourceCount)
}

if want.ChangeCount != got.ChangeCount {
t.Fatalf("ChangeCount: want %f; got %f", want.ChangeCount, got.ChangeCount)
}

if want.EventCount != got.EventCount {
t.Fatalf("EventCount: want %f; got %f", want.EventCount, got.EventCount)
}

if err := mapCompare(want.ResourceStates, got.ResourceStates); err != "" {
t.Fatalf("ResourceStates: %s", err)
}

if err := mapCompare(want.EventStates, got.EventStates); err != "" {
t.Fatalf("EventStates: %s", err)
}
})
}
}

func mapCompare(l, r map[string]float64) string {
if len(l) != len(r) {
return fmt.Sprintf("length: want %d, got %d", len(l), len(r))
}
if ir != expected {
t.Fatalf("%+v != %+v", ir, expected)

for k, want := range l {
got, ok := r[k]
if !ok {
return fmt.Sprintf("key %q is missing", k)
}

if want != got {
return fmt.Sprintf("key %q: want %f, got %f", k, want, got)
}
}

return ""
}
1 change: 1 addition & 0 deletions puppetreport/testdata/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.deb
Loading