Skip to content

Commit ba1bb59

Browse files
committed
Add QueueLength Metric
1 parent ea41bd1 commit ba1bb59

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

pkg/internal/controller/controller.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"sigs.k8s.io/controller-runtime/pkg/cache"
3131
"sigs.k8s.io/controller-runtime/pkg/client"
3232
"sigs.k8s.io/controller-runtime/pkg/handler"
33+
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics"
3334
"sigs.k8s.io/controller-runtime/pkg/predicate"
3435
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3536
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
@@ -171,6 +172,9 @@ func (c *Controller) Start(stop <-chan struct{}) error {
171172
func (c *Controller) processNextWorkItem() bool {
172173
// This code copy-pasted from the sample-Controller.
173174

175+
// Update metrics after processing each item
176+
defer c.updateMetrics()
177+
174178
obj, shutdown := c.Queue.Get()
175179
if obj == nil {
176180
// Sometimes the Queue gives us nil items when it starts up
@@ -233,3 +237,8 @@ func (c *Controller) InjectFunc(f inject.Func) error {
233237
c.SetFields = f
234238
return nil
235239
}
240+
241+
// updateMetrics updates prometheus metrics within the controller
242+
func (c *Controller) updateMetrics() {
243+
ctrlmetrics.QueueLength.WithLabelValues(c.Name).Set(float64(c.Queue.Len()))
244+
}

pkg/internal/controller/controller_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323

2424
. "github.com/onsi/ginkgo"
2525
. "github.com/onsi/gomega"
26+
"github.com/prometheus/client_golang/prometheus"
27+
dto "github.com/prometheus/client_model/go"
2628
"k8s.io/api/apps/v1"
2729
corev1 "k8s.io/api/core/v1"
2830
"k8s.io/apimachinery/pkg/types"
@@ -31,6 +33,7 @@ import (
3133
"sigs.k8s.io/controller-runtime/pkg/cache/informertest"
3234
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
3335
"sigs.k8s.io/controller-runtime/pkg/handler"
36+
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics"
3437
"sigs.k8s.io/controller-runtime/pkg/predicate"
3538
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3639
"sigs.k8s.io/controller-runtime/pkg/reconcile/reconciletest"
@@ -404,6 +407,46 @@ var _ = Describe("controller", func() {
404407
It("should create a new go routine for MaxConcurrentReconciles", func() {
405408
// TODO(community): write this test
406409
})
410+
411+
Context("should update prometheus metrics", func() {
412+
It("should requeue a Request if there is an error and continue processing items", func(done Done) {
413+
ctrlmetrics.QueueLength = prometheus.NewGaugeVec(prometheus.GaugeOpts{
414+
Name: "controller_runtime_reconcile_queue_length",
415+
Help: "Length of reconcile queue per controller",
416+
}, []string{"controller"})
417+
418+
fakeReconcile.Err = fmt.Errorf("expected error: reconcile")
419+
go func() {
420+
defer GinkgoRecover()
421+
Expect(ctrl.Start(stop)).NotTo(HaveOccurred())
422+
}()
423+
ctrl.Queue.Add(request)
424+
425+
// Reduce the jitterperiod so we don't have to wait a second before the reconcile function is rerun.
426+
ctrl.JitterPeriod = time.Millisecond
427+
428+
By("Invoking Reconciler which will give an error")
429+
Expect(<-reconciled).To(Equal(request))
430+
var queueLength dto.Metric
431+
Eventually(func() error {
432+
ctrlmetrics.QueueLength.WithLabelValues(ctrl.Name).Write(&queueLength)
433+
if queueLength.GetGauge().GetValue() != 1.0 {
434+
return fmt.Errorf("metrics not updated")
435+
}
436+
return nil
437+
}, 2.0).Should(Succeed())
438+
439+
By("Invoking Reconciler a second time without error")
440+
fakeReconcile.Err = nil
441+
Expect(<-reconciled).To(Equal(request))
442+
443+
By("Removing the item from the queue")
444+
Eventually(ctrl.Queue.Len).Should(Equal(0))
445+
Eventually(func() int { return ctrl.Queue.NumRequeues(request) }).Should(Equal(0))
446+
447+
close(done)
448+
}, 2.0)
449+
})
407450
})
408451
})
409452

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package metrics
18+
19+
import (
20+
"github.com/prometheus/client_golang/prometheus"
21+
"sigs.k8s.io/controller-runtime/pkg/metrics"
22+
)
23+
24+
var (
25+
// QueueLength is a prometheus metric which counts the current reconcile
26+
// queue length per controller
27+
QueueLength = prometheus.NewGaugeVec(prometheus.GaugeOpts{
28+
Name: "controller_runtime_reconcile_queue_length",
29+
Help: "Length of reconcile queue per controller",
30+
}, []string{"controller"})
31+
)
32+
33+
func init() {
34+
metrics.Registry.MustRegister(QueueLength)
35+
}

0 commit comments

Comments
 (0)