Skip to content

Commit b4f4f41

Browse files
authored
move condition metrics recorder to separate repository (#15)
* move condition metrics recorder to separate repository * remove readme conflicts
1 parent f561114 commit b4f4f41

File tree

5 files changed

+6
-668
lines changed

5 files changed

+6
-668
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ go.work.sum
2828
.env
2929

3030
# Editor/IDE
31-
.idea/
32-
.vscode/
31+
.idea/
32+
.vscode/

README.md

Lines changed: 4 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
A flexible, memory efficient Prometheus `GaugeVec` wrapper for managing **sets** of metrics.
44

5-
---
6-
7-
## GaugeVecSet
8-
95
The `GaugeVecSet` is a high-performance wrapper around Prometheus `GaugeVec` that enables bulk operations on series
106
by specified index and grouping labels.
117

@@ -105,162 +101,13 @@ deleted := PodPhase.DeleteByIndex("prod")
105101

106102
### GaugeVecSet: DeleteByGroup
107103

108-
Delete all series that match the given (index, group)
104+
Delete all series that match the given (index, group). The number of index and group values this method requires
105+
coincides with the number of values the gauge was initialized with, meaning you cannot specify partial values for
106+
deletion.
109107

110108
```go
111109
deleted := PodPhase.DeleteByGroup(
112110
[]string{"prod"}, // index
113111
"nginx-6f4c", // group
114112
)
115-
```
116-
117-
## ConditionMetricsRecorder
118-
119-
The `ConditionMetricsRecorder` is an implementation of `GaugeVecSet` for kubernetes operators. It enables
120-
controllers to record metrics for it's kubernetes `metav1.Conditions` on custom resources.
121-
122-
It is inspired by kube-state-metrics patterns for metrics such as `kube_pod_status_phase`. KSM exports one time series
123-
per phase for each (namespace, pod), and marks exactly one as active (1) while the others are inactive (0). This metric
124-
can be thought of as a `GaugeVecSet` with the index label `namespace`, the group `pod` and the `extra` labels
125-
(i.e. variants per group) as the options for `phase`.
126-
127-
Example:
128-
129-
```
130-
kube_pod_status_phase{namespace="default", pod="nginx", phase="Running"} 1
131-
kube_pod_status_phase{namespace="default", pod="nginx", phase="Pending"} 0
132-
kube_pod_status_phase{namespace="default", pod="nginx", phase="Failed"} 0
133-
```
134-
135-
We adopt the same pattern for controller Conditions, but we export only one time series per (status, reason) variant,
136-
meaning we delete all other variants in the group when we set the metric, ensuring the cardinality stays under control.
137-
Additionally, rather than return 1/0 indicating the activeness of the metric, we set the last transition time of the
138-
condition as the value (unix timestamp).
139-
140-
Example metric:
141-
142-
```
143-
operator_controller_condition{
144-
controller="my_controller",
145-
resource_kind="MyCR",
146-
resource_name="my-cr",
147-
resource_namespace="default",
148-
condition="Ready",
149-
status="False",
150-
reason="FailedToProvision"
151-
} 17591743210
152-
```
153-
154-
- **Index**: controller, resource_kind, resource_name, resource_namespace
155-
- **Group**: condition
156-
- **Extra**: status, reason
157-
- **Metric Value**: Unix timestamp of last transition of given condition
158-
159-
### Initialization
160-
161-
The metric should be initialized and registered once.
162-
163-
You can embed the `ControllerMetricsRecorder` in your controller's recorder.
164-
165-
```go
166-
package my_metrics
167-
168-
import (
169-
controllermetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
170-
ocg "github.com/sourcehawk/go-prometheus-gaugevecset/pkg/operator_condition_metrics"
171-
)
172-
173-
// We need this variable later to create the ConditionMetricsRecorder
174-
var OperatorConditionsGauge *ocg.OperatorConditionsGauge
175-
176-
// Initialize the operator condition gauge once
177-
func init() {
178-
OperatorConditionsGauge = ocg.NewOperatorConditionsGauge("my-operator")
179-
controllermetrics.Registry.MustRegister(OperatorConditionsGauge)
180-
}
181-
182-
// Embed in existing metrics recorder
183-
type MyControllerRecorder struct {
184-
ocg.ConditionMetricRecorder
185-
}
186-
```
187-
188-
When constructing your reconciler, initialize the condition metrics recorder with the
189-
operator conditions gauge and a unique name for each controller.
190-
191-
_cmd/main.go_
192-
```go
193-
package main
194-
195-
import (
196-
mymetrics "path/to/pkg/my_metrics"
197-
ocg "github.com/sourcehawk/go-prometheus-gaugevecset/pkg/operator_condition_metrics"
198-
)
199-
200-
func main() {
201-
// ...
202-
recorder := mymetrics.MyControllerRecorder{
203-
ConditionMetricRecorder: ocg.ConditionMetricRecorder{
204-
Controller: "my-controller", // unique name per reconciler
205-
OperatorConditionsGauge: mymetrics.OperatorConditionsGauge,
206-
},
207-
}
208-
209-
reconciler := &MyReconciler{
210-
Recorder: recorder,
211-
}
212-
// ...
213-
}
214-
```
215-
216-
## Usage
217-
218-
The easiest drop-in way to start using the metrics recorder is by creating a `SetStatusCondition` wrapper, which
219-
comes instead of `meta.SetStatusCondition`.
220-
221-
To delete the metrics for a given custom resource, simply call `RemoveConditionsFor` and pass the object.
222-
223-
```go
224-
const (
225-
kind = "MyCR"
226-
)
227-
228-
// SetStatusCondition utility function which replaces and wraps meta.SetStatusCondition calls
229-
func (r *MyReconciler) SetStatusCondition(cr *v1.MyCR, cond metav1.Condition) bool {
230-
changed := meta.SetStatusCondition(&cr.Status.Conditions, cond)
231-
if changed {
232-
// refetch the condition to get the updated version
233-
updated := meta.FindStatusCondition(cr.Status.Conditions, cond.Type)
234-
if updated != nil {
235-
r.Recorder.RecordConditionFor(
236-
kind, cr, updated.Type, string(updated.Status), updated.Reason, updated.LastTransitionTime,
237-
)
238-
}
239-
}
240-
return changed
241-
}
242-
243-
func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
244-
// Get the resource we're reconciling
245-
cr := new(v1.MyCR)
246-
if err = r.Get(ctx, req.NamespacedName, cr); err != nil {
247-
return ctrl.Result{}, client.IgnoreNotFound(err)
248-
}
249-
250-
// Remove the metrics when the CR is deleted
251-
if cr.DeletionTimestamp != nil {
252-
r.Recorder.RemoveConditionsFor(kind, cr)
253-
}
254-
255-
// ...
256-
257-
// Update the status conditions using the recorder (it records the metric if changed)
258-
if r.SetStatusCondition(cr, condition) {
259-
if err = r.Status().Update(ctx, cr); err != nil {
260-
return ctrl.Result{}, err
261-
}
262-
}
263-
264-
return ctrl.Result{}, nil
265-
}
266-
```
113+
```

pkg/operator_condition_metrics/operator_condition_metrics.go

Lines changed: 0 additions & 202 deletions
This file was deleted.

0 commit comments

Comments
 (0)