@@ -9,9 +9,12 @@ import (
9
9
"time"
10
10
11
11
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12
+ "k8s.io/apimachinery/pkg/runtime"
13
+ "k8s.io/apimachinery/pkg/runtime/schema"
12
14
13
15
pmetrics "github.com/awslabs/operatorpkg/metrics"
14
16
"github.com/awslabs/operatorpkg/object"
17
+ "github.com/awslabs/operatorpkg/option"
15
18
"github.com/samber/lo"
16
19
v1 "k8s.io/api/core/v1"
17
20
"k8s.io/apimachinery/pkg/api/errors"
@@ -24,24 +27,60 @@ import (
24
27
)
25
28
26
29
type Controller [T Object ] struct {
27
- kubeClient client.Client
28
- eventRecorder record.EventRecorder
29
- observedConditions sync.Map // map[reconcile.Request]ConditionSet
30
- terminatingObjects sync.Map // map[reconcile.Request]DeletionTimestamp
30
+ gvk schema.GroupVersionKind
31
+ kubeClient client.Client
32
+ eventRecorder record.EventRecorder
33
+ observedConditions sync.Map // map[reconcile.Request]ConditionSet
34
+ terminatingObjects sync.Map // map[reconcile.Request]DeletionTimestamp
35
+ emitDeprecatedMetrics bool
36
+ ConditionDuration pmetrics.ObservationMetric
37
+ ConditionCount pmetrics.GaugeMetric
38
+ ConditionCurrentStatusSeconds pmetrics.GaugeMetric
39
+ ConditionTransitionsTotal pmetrics.CounterMetric
40
+ TerminationCurrentTimeSeconds pmetrics.GaugeMetric
41
+ TerminationDuration pmetrics.ObservationMetric
31
42
}
32
43
33
- func NewController [T Object ](client client.Client , eventRecorder record.EventRecorder ) * Controller [T ] {
44
+ type Option struct {
45
+ // Current list of deprecated metrics
46
+ // - operator_status_condition_transitions_total
47
+ // - operator_status_condition_transition_seconds
48
+ // - operator_status_condition_current_status_seconds
49
+ // - operator_status_condition_count
50
+ // - operator_termination_current_time_seconds
51
+ // - operator_termination_duration_seconds
52
+ EmitDeprecatedMetrics bool
53
+ }
54
+
55
+ func EmitDeprecatedMetrics (o * Option ) {
56
+ o .EmitDeprecatedMetrics = true
57
+ }
58
+
59
+ func NewController [T Object ](client client.Client , eventRecorder record.EventRecorder , opts ... option.Function [Option ]) * Controller [T ] {
60
+ options := option .Resolve (opts ... )
61
+ obj := reflect .New (reflect .TypeOf (* new (T )).Elem ()).Interface ().(runtime.Object )
62
+ obj .GetObjectKind ().SetGroupVersionKind (obj .GetObjectKind ().GroupVersionKind ())
63
+ gvk := object .GVK (obj )
64
+
34
65
return & Controller [T ]{
35
- kubeClient : client ,
36
- eventRecorder : eventRecorder ,
66
+ gvk : gvk ,
67
+ kubeClient : client ,
68
+ eventRecorder : eventRecorder ,
69
+ emitDeprecatedMetrics : options .EmitDeprecatedMetrics ,
70
+ ConditionDuration : conditionDurationMetric (strings .ToLower (gvk .Kind )),
71
+ ConditionCount : conditionCountMetric (strings .ToLower (gvk .Kind )),
72
+ ConditionCurrentStatusSeconds : conditionCurrentStatusSecondsMetric (strings .ToLower (gvk .Kind )),
73
+ ConditionTransitionsTotal : conditionTransitionsTotalMetric (strings .ToLower (gvk .Kind )),
74
+ TerminationCurrentTimeSeconds : terminationCurrentTimeSecondsMetric (strings .ToLower (gvk .Kind )),
75
+ TerminationDuration : terminationDurationMetric (strings .ToLower (gvk .Kind )),
37
76
}
38
77
}
39
78
40
79
func (c * Controller [T ]) Register (_ context.Context , m manager.Manager ) error {
41
80
return controllerruntime .NewControllerManagedBy (m ).
42
81
For (object .New [T ]()).
43
82
WithOptions (controller.Options {MaxConcurrentReconciles : 10 }).
44
- Named (fmt .Sprintf ("operatorpkg.%s.status" , strings .ToLower (reflect . TypeOf ( object . New [ T ]()). Elem (). Name () ))).
83
+ Named (fmt .Sprintf ("operatorpkg.%s.status" , strings .ToLower (c . gvk . Kind ))).
45
84
Complete (c )
46
85
}
47
86
@@ -50,12 +89,12 @@ func (c *Controller[T]) Reconcile(ctx context.Context, req reconcile.Request) (r
50
89
}
51
90
52
91
type GenericObjectController [T client.Object ] struct {
53
- * Controller [* unstructuredAdapter ]
92
+ * Controller [* unstructuredAdapter [ T ] ]
54
93
}
55
94
56
- func NewGenericObjectController [T client.Object ](client client.Client , eventRecorder record.EventRecorder ) * GenericObjectController [T ] {
95
+ func NewGenericObjectController [T client.Object ](client client.Client , eventRecorder record.EventRecorder , opts ... option. Function [ Option ] ) * GenericObjectController [T ] {
57
96
return & GenericObjectController [T ]{
58
- Controller : NewController [* unstructuredAdapter ] (client , eventRecorder ),
97
+ Controller : NewController [* unstructuredAdapter [ T ]] (client , eventRecorder , opts ... ),
59
98
}
60
99
}
61
100
@@ -68,37 +107,26 @@ func (c *GenericObjectController[T]) Register(_ context.Context, m manager.Manag
68
107
}
69
108
70
109
func (c * GenericObjectController [T ]) Reconcile (ctx context.Context , req reconcile.Request ) (reconcile.Result , error ) {
71
- return c .reconcile (ctx , req , NewUnstructuredAdapter (object .New [T ]()))
110
+ return c .reconcile (ctx , req , NewUnstructuredAdapter [ T ] (object .New [T ]()))
72
111
}
73
112
74
113
func (c * Controller [T ]) reconcile (ctx context.Context , req reconcile.Request , o Object ) (reconcile.Result , error ) {
75
- gvk := object .GVK (o )
76
-
77
114
if err := c .kubeClient .Get (ctx , req .NamespacedName , o ); err != nil {
78
115
if errors .IsNotFound (err ) {
79
- ConditionCount .DeletePartialMatch (map [string ]string {
80
- pmetrics .LabelGroup : gvk .Group ,
81
- pmetrics .LabelKind : gvk .Kind ,
116
+ c .deletePartialMatchGaugeMetric (c .ConditionCount , ConditionCount , map [string ]string {
82
117
MetricLabelNamespace : req .Namespace ,
83
118
MetricLabelName : req .Name ,
84
119
})
85
- ConditionCurrentStatusSeconds .DeletePartialMatch (map [string ]string {
86
- pmetrics .LabelGroup : gvk .Group ,
87
- pmetrics .LabelKind : gvk .Kind ,
120
+ c .deletePartialMatchGaugeMetric (c .ConditionCurrentStatusSeconds , ConditionCurrentStatusSeconds , map [string ]string {
88
121
MetricLabelNamespace : req .Namespace ,
89
122
MetricLabelName : req .Name ,
90
123
})
91
- TerminationCurrentTimeSeconds . DeletePartialMatch ( map [string ]string {
124
+ c . deletePartialMatchGaugeMetric ( c . TerminationCurrentTimeSeconds , TerminationCurrentTimeSeconds , map [string ]string {
92
125
MetricLabelNamespace : req .Namespace ,
93
126
MetricLabelName : req .Name ,
94
- pmetrics .LabelGroup : gvk .Group ,
95
- pmetrics .LabelKind : gvk .Kind ,
96
127
})
97
128
if deletionTS , ok := c .terminatingObjects .Load (req ); ok {
98
- TerminationDuration .Observe (time .Since (deletionTS .(* metav1.Time ).Time ).Seconds (), map [string ]string {
99
- pmetrics .LabelGroup : gvk .Group ,
100
- pmetrics .LabelKind : gvk .Kind ,
101
- })
129
+ c .observeHistogram (c .TerminationDuration , TerminationDuration , time .Since (deletionTS .(* metav1.Time ).Time ).Seconds (), map [string ]string {})
102
130
}
103
131
return reconcile.Result {}, nil
104
132
}
@@ -114,18 +142,14 @@ func (c *Controller[T]) reconcile(ctx context.Context, req reconcile.Request, o
114
142
115
143
// Detect and record condition counts
116
144
for _ , condition := range o .GetConditions () {
117
- ConditionCount .Set (1 , map [string ]string {
118
- pmetrics .LabelGroup : gvk .Group ,
119
- pmetrics .LabelKind : gvk .Kind ,
145
+ c .setGaugeMetric (c .ConditionCount , ConditionCount , 1 , map [string ]string {
120
146
MetricLabelNamespace : req .Namespace ,
121
147
MetricLabelName : req .Name ,
122
148
pmetrics .LabelType : condition .Type ,
123
149
MetricLabelConditionStatus : string (condition .Status ),
124
150
pmetrics .LabelReason : condition .Reason ,
125
151
})
126
- ConditionCurrentStatusSeconds .Set (time .Since (condition .LastTransitionTime .Time ).Seconds (), map [string ]string {
127
- pmetrics .LabelGroup : gvk .Group ,
128
- pmetrics .LabelKind : gvk .Kind ,
152
+ c .setGaugeMetric (c .ConditionCurrentStatusSeconds , ConditionCurrentStatusSeconds , time .Since (condition .LastTransitionTime .Time ).Seconds (), map [string ]string {
129
153
MetricLabelNamespace : req .Namespace ,
130
154
MetricLabelName : req .Name ,
131
155
pmetrics .LabelType : condition .Type ,
@@ -134,28 +158,22 @@ func (c *Controller[T]) reconcile(ctx context.Context, req reconcile.Request, o
134
158
})
135
159
}
136
160
if o .GetDeletionTimestamp () != nil {
137
- TerminationCurrentTimeSeconds . Set ( time .Since (o .GetDeletionTimestamp ().Time ).Seconds (), map [string ]string {
161
+ c . setGaugeMetric ( c . TerminationCurrentTimeSeconds , TerminationCurrentTimeSeconds , time .Since (o .GetDeletionTimestamp ().Time ).Seconds (), map [string ]string {
138
162
MetricLabelNamespace : req .Namespace ,
139
163
MetricLabelName : req .Name ,
140
- pmetrics .LabelGroup : gvk .Group ,
141
- pmetrics .LabelKind : gvk .Kind ,
142
164
})
143
165
c .terminatingObjects .Store (req , o .GetDeletionTimestamp ())
144
166
}
145
167
for _ , observedCondition := range observedConditions .List () {
146
168
if currentCondition := currentConditions .Get (observedCondition .Type ); currentCondition == nil || currentCondition .Status != observedCondition .Status {
147
- ConditionCount .Delete (map [string ]string {
148
- pmetrics .LabelGroup : gvk .Group ,
149
- pmetrics .LabelKind : gvk .Kind ,
169
+ c .deleteGaugeMetric (c .ConditionCount , ConditionCount , map [string ]string {
150
170
MetricLabelNamespace : req .Namespace ,
151
171
MetricLabelName : req .Name ,
152
172
pmetrics .LabelType : observedCondition .Type ,
153
173
MetricLabelConditionStatus : string (observedCondition .Status ),
154
174
pmetrics .LabelReason : observedCondition .Reason ,
155
175
})
156
- ConditionCurrentStatusSeconds .Delete (map [string ]string {
157
- pmetrics .LabelGroup : gvk .Group ,
158
- pmetrics .LabelKind : gvk .Kind ,
176
+ c .deleteGaugeMetric (c .ConditionCurrentStatusSeconds , ConditionCurrentStatusSeconds , map [string ]string {
159
177
MetricLabelNamespace : req .Namespace ,
160
178
MetricLabelName : req .Name ,
161
179
pmetrics .LabelType : observedCondition .Type ,
@@ -186,9 +204,7 @@ func (c *Controller[T]) reconcile(ctx context.Context, req reconcile.Request, o
186
204
continue
187
205
}
188
206
// A condition transitions if it either didn't exist before or it has changed
189
- ConditionTransitionsTotal .Inc (map [string ]string {
190
- pmetrics .LabelGroup : gvk .Group ,
191
- pmetrics .LabelKind : gvk .Kind ,
207
+ c .incCounterMetric (c .ConditionTransitionsTotal , ConditionTransitionsTotal , map [string ]string {
192
208
pmetrics .LabelType : condition .Type ,
193
209
MetricLabelConditionStatus : string (condition .Status ),
194
210
pmetrics .LabelReason : condition .Reason ,
@@ -197,9 +213,7 @@ func (c *Controller[T]) reconcile(ctx context.Context, req reconcile.Request, o
197
213
continue
198
214
}
199
215
duration := condition .LastTransitionTime .Time .Sub (observedCondition .LastTransitionTime .Time ).Seconds ()
200
- ConditionDuration .Observe (duration , map [string ]string {
201
- pmetrics .LabelGroup : gvk .Group ,
202
- pmetrics .LabelKind : gvk .Kind ,
216
+ c .observeHistogram (c .ConditionDuration , ConditionDuration , duration , map [string ]string {
203
217
pmetrics .LabelType : observedCondition .Type ,
204
218
MetricLabelConditionStatus : string (observedCondition .Status ),
205
219
})
@@ -213,3 +227,48 @@ func (c *Controller[T]) reconcile(ctx context.Context, req reconcile.Request, o
213
227
}
214
228
return reconcile.Result {RequeueAfter : time .Second * 10 }, nil
215
229
}
230
+
231
+ func (c * Controller [T ]) incCounterMetric (current pmetrics.CounterMetric , deprecated pmetrics.CounterMetric , labels map [string ]string ) {
232
+ current .Inc (labels )
233
+ if c .emitDeprecatedMetrics {
234
+ labels [pmetrics .LabelKind ] = c .gvk .Kind
235
+ labels [pmetrics .LabelGroup ] = c .gvk .Group
236
+ deprecated .Inc (labels )
237
+ }
238
+ }
239
+
240
+ func (c * Controller [T ]) setGaugeMetric (current pmetrics.GaugeMetric , deprecated pmetrics.GaugeMetric , value float64 , labels map [string ]string ) {
241
+ current .Set (value , labels )
242
+ if c .emitDeprecatedMetrics {
243
+ labels [pmetrics .LabelKind ] = c .gvk .Kind
244
+ labels [pmetrics .LabelGroup ] = c .gvk .Group
245
+ deprecated .Set (value , labels )
246
+ }
247
+ }
248
+
249
+ func (c * Controller [T ]) deleteGaugeMetric (current pmetrics.GaugeMetric , deprecated pmetrics.GaugeMetric , labels map [string ]string ) {
250
+ current .Delete (labels )
251
+ if c .emitDeprecatedMetrics {
252
+ labels [pmetrics .LabelKind ] = c .gvk .Kind
253
+ labels [pmetrics .LabelGroup ] = c .gvk .Group
254
+ deprecated .Delete (labels )
255
+ }
256
+ }
257
+
258
+ func (c * Controller [T ]) deletePartialMatchGaugeMetric (current pmetrics.GaugeMetric , deprecated pmetrics.GaugeMetric , labels map [string ]string ) {
259
+ current .DeletePartialMatch (labels )
260
+ if c .emitDeprecatedMetrics {
261
+ labels [pmetrics .LabelKind ] = c .gvk .Kind
262
+ labels [pmetrics .LabelGroup ] = c .gvk .Group
263
+ deprecated .DeletePartialMatch (labels )
264
+ }
265
+ }
266
+
267
+ func (c * Controller [T ]) observeHistogram (current pmetrics.ObservationMetric , deprecated pmetrics.ObservationMetric , value float64 , labels map [string ]string ) {
268
+ current .Observe (value , labels )
269
+ if c .emitDeprecatedMetrics {
270
+ labels [pmetrics .LabelKind ] = c .gvk .Kind
271
+ labels [pmetrics .LabelGroup ] = c .gvk .Group
272
+ deprecated .Observe (value , labels )
273
+ }
274
+ }
0 commit comments