From 3c0441b94c9188419f34a6791f0a6f7b6ef276d3 Mon Sep 17 00:00:00 2001 From: Peter Wilhelm Date: Sun, 7 Jan 2024 18:04:47 -0600 Subject: [PATCH] Add metrics and progress report capabilities for deferred items seperate from other statuses --- pkg/metrics/metrics.go | 31 +++++++++++++-------- pkg/notifications/json.go | 13 +++++---- pkg/notifications/json_test.go | 3 +- pkg/notifications/preview/data/data.go | 2 ++ pkg/notifications/preview/data/report.go | 32 ++++++++++++++-------- pkg/session/container_status.go | 3 ++ pkg/session/progress.go | 5 ++++ pkg/session/report.go | 35 ++++++++++++++++-------- pkg/types/report.go | 1 + tplprev/main.go | 2 +- 10 files changed, 84 insertions(+), 43 deletions(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index b681733..0c177e9 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -10,19 +10,21 @@ var metrics *Metrics // Metric is the data points of a single scan type Metric struct { - Scanned int - Updated int - Failed int + Scanned int + Updated int + Deferred int + Failed int } // Metrics is the handler processing all individual scan metrics type Metrics struct { - channel chan *Metric - scanned prometheus.Gauge - updated prometheus.Gauge - failed prometheus.Gauge - total prometheus.Counter - skipped prometheus.Counter + channel chan *Metric + scanned prometheus.Gauge + updated prometheus.Gauge + deferred prometheus.Gauge + failed prometheus.Gauge + total prometheus.Counter + skipped prometheus.Counter } // NewMetric returns a Metric with the counts taken from the appropriate types.Report fields @@ -30,8 +32,9 @@ func NewMetric(report types.Report) *Metric { return &Metric{ Scanned: len(report.Scanned()), // Note: This is for backwards compatibility. ideally, stale containers should be counted separately - Updated: len(report.Updated()) + len(report.Stale()), - Failed: len(report.Failed()), + Updated: len(report.Updated()) + len(report.Stale()), + Deferred: len(report.Deferred()), + Failed: len(report.Failed()), } } @@ -60,6 +63,10 @@ func Default() *Metrics { Name: "watchtower_containers_updated", Help: "Number of containers updated by watchtower during the last scan", }), + deferred: promauto.NewGauge(prometheus.GaugeOpts{ + Name: "watchtower_containers_deferred", + Help: "Number of containers deferred by watchtower during the last scan", + }), failed: promauto.NewGauge(prometheus.GaugeOpts{ Name: "watchtower_containers_failed", Help: "Number of containers where update failed during the last scan", @@ -95,6 +102,7 @@ func (metrics *Metrics) HandleUpdate(channel <-chan *Metric) { metrics.skipped.Inc() metrics.scanned.Set(0) metrics.updated.Set(0) + metrics.deferred.Set(0) metrics.failed.Set(0) continue } @@ -102,6 +110,7 @@ func (metrics *Metrics) HandleUpdate(channel <-chan *Metric) { metrics.total.Inc() metrics.scanned.Set(float64(change.Scanned)) metrics.updated.Set(float64(change.Updated)) + metrics.deferred.Set(float64(change.Deferred)) metrics.failed.Set(float64(change.Failed)) } } diff --git a/pkg/notifications/json.go b/pkg/notifications/json.go index 20da92b..92437eb 100644 --- a/pkg/notifications/json.go +++ b/pkg/notifications/json.go @@ -23,12 +23,13 @@ func (d Data) MarshalJSON() ([]byte, error) { var report jsonMap if d.Report != nil { report = jsonMap{ - `scanned`: marshalReports(d.Report.Scanned()), - `updated`: marshalReports(d.Report.Updated()), - `failed`: marshalReports(d.Report.Failed()), - `skipped`: marshalReports(d.Report.Skipped()), - `stale`: marshalReports(d.Report.Stale()), - `fresh`: marshalReports(d.Report.Fresh()), + `scanned`: marshalReports(d.Report.Scanned()), + `updated`: marshalReports(d.Report.Updated()), + `deferred`: marshalReports(d.Report.Deferred()), + `failed`: marshalReports(d.Report.Failed()), + `skipped`: marshalReports(d.Report.Skipped()), + `stale`: marshalReports(d.Report.Stale()), + `fresh`: marshalReports(d.Report.Fresh()), } } diff --git a/pkg/notifications/json_test.go b/pkg/notifications/json_test.go index ef30c59..fca1231 100644 --- a/pkg/notifications/json_test.go +++ b/pkg/notifications/json_test.go @@ -21,6 +21,7 @@ var _ = Describe("JSON template", func() { ], "host": "Mock", "report": { + "deferred": [], "failed": [ { "currentImageId": "01d210000000", @@ -110,7 +111,7 @@ var _ = Describe("JSON template", func() { }, "title": "Watchtower updates on Mock" }` - data := mockDataFromStates(s.UpdatedState, s.FreshState, s.FailedState, s.SkippedState, s.UpdatedState) + data := mockDataFromStates(s.UpdatedState, s.DeferredState, s.FreshState, s.FailedState, s.SkippedState, s.UpdatedState) Expect(getTemplatedResult(`json.v1`, false, data)).To(MatchJSON(expected)) }) }) diff --git a/pkg/notifications/preview/data/data.go b/pkg/notifications/preview/data/data.go index 4a002ed..295d097 100644 --- a/pkg/notifications/preview/data/data.go +++ b/pkg/notifications/preview/data/data.go @@ -72,6 +72,8 @@ func (pb *previewData) addContainer(c containerStatus) { pb.report.scanned = append(pb.report.scanned, &c) case UpdatedState: pb.report.updated = append(pb.report.updated, &c) + case DeferredState: + pb.report.deferred = append(pb.report.deferred, &c) case FailedState: pb.report.failed = append(pb.report.failed, &c) case SkippedState: diff --git a/pkg/notifications/preview/data/report.go b/pkg/notifications/preview/data/report.go index 2c8627f..b24674b 100644 --- a/pkg/notifications/preview/data/report.go +++ b/pkg/notifications/preview/data/report.go @@ -10,12 +10,13 @@ import ( type State string const ( - ScannedState State = "scanned" - UpdatedState State = "updated" - FailedState State = "failed" - SkippedState State = "skipped" - StaleState State = "stale" - FreshState State = "fresh" + ScannedState State = "scanned" + UpdatedState State = "updated" + DeferredState State = "deferred" + FailedState State = "failed" + SkippedState State = "skipped" + StaleState State = "stale" + FreshState State = "fresh" ) // StatesFromString parses a string of state characters and returns a slice of the corresponding report states @@ -27,6 +28,8 @@ func StatesFromString(str string) []State { states = append(states, ScannedState) case 'u': states = append(states, UpdatedState) + case 'd': + states = append(states, DeferredState) case 'e': states = append(states, FailedState) case 'k': @@ -43,12 +46,13 @@ func StatesFromString(str string) []State { } type report struct { - scanned []types.ContainerReport - updated []types.ContainerReport - failed []types.ContainerReport - skipped []types.ContainerReport - stale []types.ContainerReport - fresh []types.ContainerReport + scanned []types.ContainerReport + updated []types.ContainerReport + deferred []types.ContainerReport + failed []types.ContainerReport + skipped []types.ContainerReport + stale []types.ContainerReport + fresh []types.ContainerReport } func (r *report) Scanned() []types.ContainerReport { @@ -57,6 +61,9 @@ func (r *report) Scanned() []types.ContainerReport { func (r *report) Updated() []types.ContainerReport { return r.updated } +func (r *report) Deferred() []types.ContainerReport { + return r.deferred +} func (r *report) Failed() []types.ContainerReport { return r.failed } @@ -87,6 +94,7 @@ func (r *report) All() []types.ContainerReport { } appendUnique(r.updated) + appendUnique(r.deferred) appendUnique(r.failed) appendUnique(r.skipped) appendUnique(r.stale) diff --git a/pkg/session/container_status.go b/pkg/session/container_status.go index 8313da1..df10ec1 100644 --- a/pkg/session/container_status.go +++ b/pkg/session/container_status.go @@ -12,6 +12,7 @@ const ( SkippedState ScannedState UpdatedState + DeferredState FailedState FreshState StaleState @@ -70,6 +71,8 @@ func (u *ContainerStatus) State() string { return "Scanned" case UpdatedState: return "Updated" + case DeferredState: + return "Deferred" case FailedState: return "Failed" case FreshState: diff --git a/pkg/session/progress.go b/pkg/session/progress.go index 57069be..0c67c48 100644 --- a/pkg/session/progress.go +++ b/pkg/session/progress.go @@ -50,6 +50,11 @@ func (m Progress) MarkForUpdate(containerID types.ContainerID) { m[containerID].state = UpdatedState } +// MarkForUpdate marks the container identified by containerID for deferral +func (m Progress) MarkDeferred(containerID types.ContainerID) { + m[containerID].state = DeferredState +} + // Report creates a new Report from a Progress instance func (m Progress) Report() types.Report { return NewReport(m) diff --git a/pkg/session/report.go b/pkg/session/report.go index 707eb91..cbcf1db 100644 --- a/pkg/session/report.go +++ b/pkg/session/report.go @@ -7,12 +7,13 @@ import ( ) type report struct { - scanned []types.ContainerReport - updated []types.ContainerReport - failed []types.ContainerReport - skipped []types.ContainerReport - stale []types.ContainerReport - fresh []types.ContainerReport + scanned []types.ContainerReport + updated []types.ContainerReport + deferred []types.ContainerReport + failed []types.ContainerReport + skipped []types.ContainerReport + stale []types.ContainerReport + fresh []types.ContainerReport } func (r *report) Scanned() []types.ContainerReport { @@ -21,6 +22,9 @@ func (r *report) Scanned() []types.ContainerReport { func (r *report) Updated() []types.ContainerReport { return r.updated } +func (r *report) Deferred() []types.ContainerReport { + return r.deferred +} func (r *report) Failed() []types.ContainerReport { return r.failed } @@ -50,6 +54,7 @@ func (r *report) All() []types.ContainerReport { } appendUnique(r.updated) + appendUnique(r.deferred) appendUnique(r.failed) appendUnique(r.skipped) appendUnique(r.stale) @@ -64,12 +69,13 @@ func (r *report) All() []types.ContainerReport { // NewReport creates a types.Report from the supplied Progress func NewReport(progress Progress) types.Report { report := &report{ - scanned: []types.ContainerReport{}, - updated: []types.ContainerReport{}, - failed: []types.ContainerReport{}, - skipped: []types.ContainerReport{}, - stale: []types.ContainerReport{}, - fresh: []types.ContainerReport{}, + scanned: []types.ContainerReport{}, + updated: []types.ContainerReport{}, + deferred: []types.ContainerReport{}, + failed: []types.ContainerReport{}, + skipped: []types.ContainerReport{}, + stale: []types.ContainerReport{}, + fresh: []types.ContainerReport{}, } for _, update := range progress { @@ -88,9 +94,13 @@ func NewReport(progress Progress) types.Report { switch update.state { case UpdatedState: report.updated = append(report.updated, update) + case DeferredState: + report.deferred = append(report.deferred, update) case FailedState: report.failed = append(report.failed, update) default: + // TODO: should this be changed to something lke UnknownState since it shouldn't be possible for a container + // to be stale but its state to not be either UpdatedState, DeferredState, or FailedState? update.state = StaleState report.stale = append(report.stale, update) } @@ -98,6 +108,7 @@ func NewReport(progress Progress) types.Report { sort.Sort(sortableContainers(report.scanned)) sort.Sort(sortableContainers(report.updated)) + sort.Sort(sortableContainers(report.deferred)) sort.Sort(sortableContainers(report.failed)) sort.Sort(sortableContainers(report.skipped)) sort.Sort(sortableContainers(report.stale)) diff --git a/pkg/types/report.go b/pkg/types/report.go index f454fc6..5f6540c 100644 --- a/pkg/types/report.go +++ b/pkg/types/report.go @@ -4,6 +4,7 @@ package types type Report interface { Scanned() []ContainerReport Updated() []ContainerReport + Deferred() []ContainerReport Failed() []ContainerReport Skipped() []ContainerReport Stale() []ContainerReport diff --git a/tplprev/main.go b/tplprev/main.go index 120f968..6b62d8f 100644 --- a/tplprev/main.go +++ b/tplprev/main.go @@ -18,7 +18,7 @@ func main() { var states string var entries string - flag.StringVar(&states, "states", "cccuuueeekkktttfff", "sCanned, Updated, failEd, sKipped, sTale, Fresh") + flag.StringVar(&states, "states", "cccuuudddeeekkktttfff", "sCanned, Updated, Deferred, failEd, sKipped, sTale, Fresh") flag.StringVar(&entries, "entries", "ewwiiidddd", "Fatal,Error,Warn,Info,Debug,Trace") flag.Parse()