Skip to content

Commit a0f4e21

Browse files
committed
feat(metrics): add image_last_monitor_age_minutes histogram metric
1 parent 633bd17 commit a0f4e21

File tree

7 files changed

+187
-37
lines changed

7 files changed

+187
-37
lines changed

go.mod

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ require (
1111
github.com/cespare/xxhash/v2 v2.3.0
1212
github.com/distribution/reference v0.6.0
1313
github.com/google/go-containerregistry v0.20.3
14+
github.com/obalunenko/getenv v1.14.1
1415
github.com/onsi/ginkgo/v2 v2.23.3
1516
github.com/onsi/gomega v1.36.3
1617
github.com/prometheus/client_golang v1.22.0
17-
go.uber.org/zap v1.27.0
1818
k8s.io/api v0.32.3
1919
k8s.io/apimachinery v0.32.3
2020
k8s.io/client-go v1.5.2
@@ -89,15 +89,16 @@ require (
8989
go.opentelemetry.io/otel/trace v1.35.0 // indirect
9090
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
9191
go.uber.org/multierr v1.11.0 // indirect
92-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
93-
golang.org/x/net v0.39.0 // indirect
92+
go.uber.org/zap v1.27.0 // indirect
93+
golang.org/x/exp v0.0.0-20250808145144-a408d31f581a // indirect
94+
golang.org/x/net v0.43.0 // indirect
9495
golang.org/x/oauth2 v0.29.0 // indirect
95-
golang.org/x/sync v0.13.0 // indirect
96-
golang.org/x/sys v0.32.0 // indirect
97-
golang.org/x/term v0.31.0 // indirect
98-
golang.org/x/text v0.24.0 // indirect
96+
golang.org/x/sync v0.16.0 // indirect
97+
golang.org/x/sys v0.35.0 // indirect
98+
golang.org/x/term v0.34.0 // indirect
99+
golang.org/x/text v0.28.0 // indirect
99100
golang.org/x/time v0.11.0 // indirect
100-
golang.org/x/tools v0.32.0 // indirect
101+
golang.org/x/tools v0.36.0 // indirect
101102
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
102103
google.golang.org/genproto/googleapis/api v0.0.0-20250407143221-ac9807e6c755 // indirect
103104
google.golang.org/genproto/googleapis/rpc v0.0.0-20250407143221-ac9807e6c755 // indirect

go.sum

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
109109
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
110110
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
111111
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
112+
github.com/obalunenko/getenv v1.14.1 h1:nFwG6PMKWKNBWnvlCbIOLgYdj/sSerbLZ9k3Is26HOU=
113+
github.com/obalunenko/getenv v1.14.1/go.mod h1:O+JIpb//raW/4Mx6sno6lHckuFn7zbagod0KkyDHUX0=
112114
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
113115
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
114116
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
@@ -186,43 +188,43 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
186188
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
187189
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
188190
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
189-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
190-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
191+
golang.org/x/exp v0.0.0-20250808145144-a408d31f581a h1:Y+7uR/b1Mw2iSXZ3G//1haIiSElDQZ8KWh0h+sZPG90=
192+
golang.org/x/exp v0.0.0-20250808145144-a408d31f581a/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
191193
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
192194
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
193195
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
194196
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
195197
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
196198
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
197-
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
198-
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
199+
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
200+
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
199201
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
200202
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
201203
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
202204
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
203205
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
204-
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
205-
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
206+
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
207+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
206208
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
207209
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
208210
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
209211
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
210-
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
211-
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
212-
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
213-
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
212+
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
213+
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
214+
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
215+
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
214216
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
215217
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
216-
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
217-
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
218+
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
219+
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
218220
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
219221
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
220222
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
221223
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
222224
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
223225
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
224-
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
225-
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
226+
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
227+
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
226228
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
227229
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
228230
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

helm/kube-image-keeper/templates/manager-deployment.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,35 @@ spec:
4646
- -zap-log-level={{ .Values.manager.verbosity }}
4747
- -unused-image-ttl={{ .Values.unusedImageTTL }}
4848
env:
49+
# registry monitor default spec
4950
- name: KUIK_REGISTRY_MONITOR_DEFAULT_INTERVAL
5051
value: "{{ .Values.registryMonitors.defaultSpec.interval }}"
5152
- name: KUIK_REGISTRY_MONITOR_DEFAULT_MAX_PER_INTERVAL
5253
value: "{{ .Values.registryMonitors.defaultSpec.maxPerInterval }}"
5354
- name: KUIK_REGISTRY_MONITOR_DEFAULT_PARALLEL
5455
value: "{{ .Values.registryMonitors.defaultSpec.parallel }}"
56+
# image_last_monitor_age_minutes metric buckets
57+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_TYPE
58+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.type }}"
59+
{{- if eq .Values.metrics.imageLastMonitorAgeMinutesBuckets.type "exponential" }}
60+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_EXPONENTIAL_START
61+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.exponential.start }}"
62+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_EXPONENTIAL_FACTOR
63+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.exponential.factor }}"
64+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_EXPONENTIAL_COUNT
65+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.exponential.count }}"
66+
{{- else if eq .Values.metrics.imageLastMonitorAgeMinutesBuckets.type "exponentialRange" }}
67+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_EXPONENTIAL_RANGE_MIN
68+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.exponentialRange.min }}"
69+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_EXPONENTIAL_RANGE_MAX
70+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.exponentialRange.max }}"
71+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_EXPONENTIAL_RANGE_COUNT
72+
value: "{{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.exponentialRange.count }}"
73+
{{- else if eq .Values.metrics.imageLastMonitorAgeMinutesBuckets.type "custom" }}
74+
- name: KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_CUSTOM
75+
value: {{ .Values.metrics.imageLastMonitorAgeMinutesBuckets.custom | join "," }}
76+
{{- end }}
77+
# custom environment variables
5578
{{- range .Values.manager.env }}
5679
- name: {{ .name }}
5780
value: {{ .value }}

helm/kube-image-keeper/values.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,23 @@ registryMonitors:
107107

108108
# -- Delay in days before deleting an Image not used by any pod
109109
unusedImageTTL: 30
110+
111+
metrics:
112+
imageLastMonitorAgeMinutesBuckets:
113+
# -- Bucket generation method (can be one of exponential, exponentialRange or custom)
114+
type: exponential
115+
# -- See https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#ExponentialBuckets
116+
exponential:
117+
# -- Default is 10m
118+
start: 10
119+
factor: 2
120+
count: 6
121+
# -- See https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#ExponentialBucketsRange
122+
exponentialRange:
123+
# Default is 10m
124+
min: 10
125+
# Default is 6h
126+
max: 720
127+
count: 6
128+
# -- List of buckets to create in minutes, ommiting +inf
129+
custom: [10, 30, 60, 120, 180, 360]

internal/controller/collector.go

Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@ package controller
22

33
import (
44
"context"
5+
"errors"
6+
"fmt"
57
"strconv"
8+
"time"
69

710
kuikv1alpha1 "github.com/enix/kube-image-keeper/api/kuik/v1alpha1"
811
"github.com/enix/kube-image-keeper/internal/info"
12+
"github.com/obalunenko/getenv"
13+
"github.com/obalunenko/getenv/option"
914
"github.com/prometheus/client_golang/prometheus"
1015
"sigs.k8s.io/controller-runtime/pkg/client"
1116
"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -16,6 +21,7 @@ import (
1621
type kuikMetrics struct {
1722
collectors []prometheus.Collector
1823
monitoringTasks *prometheus.CounterVec
24+
// imageLastMonitorHistogram prometheus.Histogram
1925
}
2026

2127
var Metrics kuikMetrics
@@ -64,7 +70,7 @@ func (m *kuikMetrics) Register(elected <-chan struct{}, client client.Client) {
6470
)
6571
m.addCollector(m.monitoringTasks)
6672

67-
m.addCollector(NewGenericCollector(
73+
m.addCollector(NewGenericCollectorFunc(
6874
prometheus.Opts{
6975
Namespace: info.MetricsNamespace,
7076
Subsystem: subsystemRegistryMonitor,
@@ -106,7 +112,7 @@ func (m *kuikMetrics) Register(elected <-chan struct{}, client client.Client) {
106112
}
107113
}))
108114

109-
m.addCollector(NewGenericCollector(
115+
m.addCollector(NewGenericCollectorFunc(
110116
prometheus.Opts{
111117
Namespace: info.MetricsNamespace,
112118
Subsystem: subsystemRegistryMonitor,
@@ -131,6 +137,44 @@ func (m *kuikMetrics) Register(elected <-chan struct{}, client client.Client) {
131137
}
132138
}))
133139

140+
imageLastMonitorHistogramOpts := prometheus.Opts{
141+
Namespace: info.MetricsNamespace,
142+
Subsystem: subsystemRegistryMonitor,
143+
Name: "image_last_monitor_age_minutes",
144+
Help: "Histogram of image last monitor age in minutes",
145+
}
146+
imageLastMonitorHistogramLabels := []string{"registry"}
147+
imageLastMonitorHistogramBuckets, err := m.getImageLastMonitorAgeMinutesBuckets()
148+
if err != nil {
149+
// TODO: handle error properly: return it to the caller
150+
panic(err)
151+
}
152+
// TODO: refactor NewGenericCollector to avoid duplicating Opts and labels usage
153+
m.addCollector(NewGenericCollector(imageLastMonitorHistogramOpts, prometheus.GaugeValue, imageLastMonitorHistogramLabels, func(ch chan<- prometheus.Metric) {
154+
imageList := &kuikv1alpha1.ImageList{}
155+
if err := client.List(context.Background(), imageList); err != nil {
156+
return
157+
}
158+
159+
histogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{
160+
Namespace: imageLastMonitorHistogramOpts.Namespace,
161+
Subsystem: imageLastMonitorHistogramOpts.Subsystem,
162+
Name: imageLastMonitorHistogramOpts.Name,
163+
Help: imageLastMonitorHistogramOpts.Help,
164+
Buckets: imageLastMonitorHistogramBuckets,
165+
}, imageLastMonitorHistogramLabels)
166+
167+
now := time.Now()
168+
for _, image := range imageList.Items {
169+
if image.Status.Upstream.LastMonitor.IsZero() {
170+
continue
171+
}
172+
histogram.WithLabelValues(image.Spec.Registry).Observe(now.Sub(image.Status.Upstream.LastMonitor.Time).Minutes())
173+
}
174+
175+
histogram.Collect(ch)
176+
}))
177+
134178
metrics.Registry.MustRegister(m.collectors...)
135179
}
136180

@@ -147,27 +191,69 @@ func (m *kuikMetrics) InitMonitoringTaskRegistry(registry string) {
147191
}
148192
}
149193

150-
func (m *kuikMetrics) MonitoringTaskCompleted(registry string, status kuikv1alpha1.ImageStatusUpstream, unused bool) {
151-
m.monitoringTasks.WithLabelValues(registry, status.ToString(), strconv.FormatBool(unused)).Inc()
194+
func (m *kuikMetrics) MonitoringTaskCompleted(registry string, status kuikv1alpha1.ImageStatus, unused bool) {
195+
m.monitoringTasks.WithLabelValues(registry, status.Upstream.Status.ToString(), strconv.FormatBool(unused)).Inc()
196+
}
197+
198+
func (m *kuikMetrics) getImageLastMonitorAgeMinutesBuckets() ([]float64, error) {
199+
envPrefix := "KUIK_METRICS_IMAGE_LAST_MONITOR_AGE_MINUTES_BUCKETS_"
200+
switch bucketsType := getenv.EnvOrDefault(envPrefix+"TYPE", "custom"); bucketsType {
201+
case "exponential":
202+
envPrefix += "EXPONENTIAL_"
203+
start, err := getenv.Env[float64](envPrefix + "START")
204+
if err != nil {
205+
return nil, err
206+
}
207+
factor, err := getenv.Env[float64](envPrefix + "FACTOR")
208+
if err != nil {
209+
return nil, err
210+
}
211+
count, err := getenv.Env[int](envPrefix + "COUNT")
212+
if err != nil {
213+
return nil, err
214+
}
215+
return prometheus.ExponentialBuckets(start, factor, count), nil
216+
case "exponentialRange":
217+
envPrefix += "EXPONENTIAL_RANGE_"
218+
minBucket, err := getenv.Env[float64](envPrefix + "MIN")
219+
if err != nil {
220+
return nil, err
221+
}
222+
maxBucket, err := getenv.Env[float64](envPrefix + "MAX")
223+
if err != nil {
224+
return nil, err
225+
}
226+
count, err := getenv.Env[int](envPrefix + "COUNT")
227+
if err != nil {
228+
return nil, err
229+
}
230+
return prometheus.ExponentialBucketsRange(minBucket, maxBucket, count), nil
231+
case "custom":
232+
buckets, err := getenv.Env[[]float64](envPrefix+"CUSTOM", option.WithSeparator(","))
233+
if err != nil && errors.Is(err, getenv.ErrNotSet) {
234+
return []float64{2, 10}, nil
235+
}
236+
return buckets, err
237+
default:
238+
return nil, fmt.Errorf("invalid %s: %s", envPrefix+"TYPE", bucketsType)
239+
}
152240
}
153241

154242
type GenericCollector struct {
155-
desc *prometheus.Desc
156-
valueType prometheus.ValueType
157-
collectorCallback func(collect func(value float64, labels ...string))
243+
desc *prometheus.Desc
244+
valueType prometheus.ValueType
245+
callback func(ch chan<- prometheus.Metric)
158246
}
159247

160248
func (g *GenericCollector) Describe(ch chan<- *prometheus.Desc) {
161249
ch <- g.desc
162250
}
163251

164252
func (g *GenericCollector) Collect(ch chan<- prometheus.Metric) {
165-
g.collectorCallback(func(value float64, labels ...string) {
166-
ch <- prometheus.MustNewConstMetric(g.desc, g.valueType, value, labels...)
167-
})
253+
g.callback(ch)
168254
}
169255

170-
func NewGenericCollector(opts prometheus.Opts, valueType prometheus.ValueType, labels []string, collectorCallback func(collect func(value float64, labels ...string))) prometheus.Collector {
256+
func NewEmptyGenericCollector(opts prometheus.Opts, valueType prometheus.ValueType, labels []string) *GenericCollector {
171257
desc := prometheus.NewDesc(
172258
prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
173259
opts.Help,
@@ -176,8 +262,25 @@ func NewGenericCollector(opts prometheus.Opts, valueType prometheus.ValueType, l
176262
)
177263

178264
return &GenericCollector{
179-
desc: desc,
180-
valueType: valueType,
181-
collectorCallback: collectorCallback,
265+
desc: desc,
266+
valueType: valueType,
182267
}
183268
}
269+
270+
func NewGenericCollector(opts prometheus.Opts, valueType prometheus.ValueType, labels []string, collectorCallback func(ch chan<- prometheus.Metric)) prometheus.Collector {
271+
collector := NewEmptyGenericCollector(opts, valueType, labels)
272+
collector.callback = collectorCallback
273+
return collector
274+
}
275+
276+
func NewGenericCollectorFunc(opts prometheus.Opts, valueType prometheus.ValueType, labels []string, callback func(collect func(value float64, labels ...string))) prometheus.Collector {
277+
collector := NewGenericCollector(opts, valueType, labels, nil).(*GenericCollector)
278+
279+
collector.callback = func(ch chan<- prometheus.Metric) {
280+
callback(func(value float64, labels ...string) {
281+
ch <- prometheus.MustNewConstMetric(collector.desc, collector.valueType, value, labels...)
282+
})
283+
}
284+
285+
return collector
286+
}

internal/controller/core/pod_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ func (r *PodReconciler) SetupWithManager(mgr ctrl.Manager) error {
143143
Parallel: 1,
144144
}
145145

146+
// TODO: refactor using github.com/obalunenko/getenv
146147
if env := os.Getenv("KUIK_REGISTRY_MONITOR_DEFAULT_INTERVAL"); env != "" {
147148
interval, err := time.ParseDuration(env)
148149
if err != nil {

internal/controller/kuik/registrymonitor_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (r *RegistryMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Requ
123123
}
124124
logImage.Info("image monitored with success")
125125
// TODO: this should be done after task.Wait()
126-
kuikcontroller.Metrics.MonitoringTaskCompleted(registryMonitor.Spec.Registry, image.Status.Upstream.Status, len(image.Status.UsedByPods.Items) == 0)
126+
kuikcontroller.Metrics.MonitoringTaskCompleted(registryMonitor.Spec.Registry, image.Status, len(image.Status.UsedByPods.Items) == 0)
127127
})
128128

129129
go func() {

0 commit comments

Comments
 (0)