1 package metrics
2
3 import (
4 "context"
5 "errors"
6 "time"
7
8 "github.com/prometheus/client_golang/prometheus"
9 "k8s.io/apimachinery/pkg/runtime"
10 "k8s.io/client-go/tools/reference"
11 ctrl "sigs.k8s.io/controller-runtime"
12 ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
13
14 "edge-infra.dev/pkg/k8s/meta/status"
15 "edge-infra.dev/pkg/k8s/runtime/conditions"
16 )
17
18
19
20
21 type Metrics struct {
22 Scheme *runtime.Scheme
23 Recorder *Recorder
24 }
25
26
27
28 func New(mgr ctrl.Manager, prefix string, options ...Option) Metrics {
29 rec := NewRecorder(prefix, options...)
30
31 ctrlmetrics.Registry.MustRegister(rec.Collectors()...)
32 return Metrics{
33 Scheme: mgr.GetScheme(),
34 Recorder: rec,
35 }
36 }
37
38 func (m Metrics) RecordDuration(ctx context.Context, obj conditions.Getter, startTime time.Time) {
39 if m.Recorder == nil {
40 return
41 }
42
43 ref, err := reference.GetReference(m.Scheme, obj)
44 if err != nil {
45 ctrl.LoggerFrom(ctx).Error(
46 err, "unable to get object reference to record duration",
47 )
48 return
49 }
50 m.Recorder.RecordDuration(*ref, startTime)
51 }
52
53
54 func (m Metrics) RecordSuspend(ctx context.Context, obj conditions.Getter, suspend bool) {
55 if m.Recorder == nil {
56 return
57 }
58
59 ref, err := reference.GetReference(m.Scheme, obj)
60 if err != nil {
61 ctrl.LoggerFrom(ctx).Error(
62 err, "unable to get object reference to record suspension",
63 )
64 return
65 }
66 if err := m.Recorder.RecordSuspend(*ref, suspend); err != nil {
67 ctrl.LoggerFrom(ctx).Error(
68 err, "unable to record suspension",
69 )
70 return
71 }
72 }
73
74
75
76
77
78
79
80 func (m Metrics) RecordConditionsWithReason(ctx context.Context, obj conditions.Getter) {
81 if m.Recorder == nil {
82 return
83 }
84
85
86
87 if m.Recorder.reconcileConditionGaugeWithReason == nil {
88 ctrl.LoggerFrom(ctx).Error(
89 errors.New("gauge not set"), "reconcileConditionGaugeWithReason not set",
90 )
91 return
92 }
93
94 ref, err := reference.GetReference(m.Scheme, obj)
95 if err != nil {
96 ctrl.LoggerFrom(ctx).Error(
97 err, "unable to get object reference to record condition metric",
98 )
99 return
100 }
101
102 labels := prometheus.Labels{
103 "kind": ref.Kind,
104 "name": ref.Name,
105 "namespace": ref.Namespace,
106 }
107
108
109 m.Recorder.reconcileConditionGaugeWithReason.DeletePartialMatch(labels)
110
111 conds := conditions.GetConditions(obj)
112 for _, c := range conds {
113 if err := m.Recorder.RecordConditionWithReason(*ref, c, !obj.GetDeletionTimestamp().IsZero()); err != nil {
114 ctrl.LoggerFrom(ctx).Error(
115 err, "unable to record condition",
116 )
117 return
118 }
119 }
120 }
121
122
123 func (m Metrics) RecordCondition(ctx context.Context, obj conditions.Getter, conditionType string) {
124 if m.Recorder == nil {
125 return
126 }
127
128 ref, err := reference.GetReference(m.Scheme, obj)
129 if err != nil {
130 ctrl.LoggerFrom(ctx).Error(
131 err, "unable to get object reference to record condition metric",
132 )
133 }
134 rc := conditions.Get(obj, conditionType)
135 if rc == nil {
136 rc = conditions.UnknownCondition(conditionType, "", "")
137 }
138 if err := m.Recorder.RecordCondition(*ref, *rc, !obj.GetDeletionTimestamp().IsZero()); err != nil {
139 ctrl.LoggerFrom(ctx).Error(
140 err, "unable to record condition",
141 )
142 return
143 }
144 }
145
146
147 func (m Metrics) RecordReadiness(ctx context.Context, obj conditions.Getter) {
148 m.RecordCondition(ctx, obj, status.ReadyCondition)
149 }
150
151
152 func (m Metrics) RecordReconciling(ctx context.Context, obj conditions.Getter) {
153 m.RecordCondition(ctx, obj, status.ReconcilingCondition)
154 }
155
156
157 func (m Metrics) RecordStalled(ctx context.Context, obj conditions.Getter) {
158 m.RecordCondition(ctx, obj, status.StalledCondition)
159 }
160
View as plain text