Skip to content

Commit 5d87371

Browse files
committed
Add metrics listener to manager
1 parent 3ba4257 commit 5d87371

File tree

8 files changed

+153
-3
lines changed

8 files changed

+153
-3
lines changed

pkg/builder/builder_suite_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
. "github.com/onsi/gomega"
2424
"k8s.io/client-go/rest"
2525
"sigs.k8s.io/controller-runtime/pkg/envtest"
26+
"sigs.k8s.io/controller-runtime/pkg/metrics"
2627
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
2728
)
2829

@@ -43,9 +44,15 @@ var _ = BeforeSuite(func(done Done) {
4344
cfg, err = testenv.Start()
4445
Expect(err).NotTo(HaveOccurred())
4546

47+
// Prevent the metrics listener being created
48+
metrics.DefaultBindAddress = "0"
49+
4650
close(done)
4751
}, 60)
4852

4953
var _ = AfterSuite(func() {
5054
testenv.Stop()
55+
56+
// Put the DefaultBindAddress back
57+
metrics.DefaultBindAddress = ":8080"
5158
})

pkg/controller/controller_suite_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"k8s.io/client-go/kubernetes"
2525
"k8s.io/client-go/rest"
2626
"sigs.k8s.io/controller-runtime/pkg/envtest"
27+
"sigs.k8s.io/controller-runtime/pkg/metrics"
2728
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
2829
)
2930

@@ -48,9 +49,15 @@ var _ = BeforeSuite(func(done Done) {
4849
clientset, err = kubernetes.NewForConfig(cfg)
4950
Expect(err).NotTo(HaveOccurred())
5051

52+
// Prevent the metrics listener being created
53+
metrics.DefaultBindAddress = "0"
54+
5155
close(done)
5256
}, 60)
5357

5458
var _ = AfterSuite(func() {
5559
testenv.Stop()
60+
61+
// Put the DefaultBindAddress back
62+
metrics.DefaultBindAddress = ":8080"
5663
})

pkg/manager/internal.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package manager
1818

1919
import (
2020
"fmt"
21+
"net"
2122
"sync"
2223
"time"
2324

@@ -70,6 +71,9 @@ type controllerManager struct {
7071
// mapper is used to map resources to kind, and map kind and version.
7172
mapper meta.RESTMapper
7273

74+
// metricsListener is used to serve prometheus metrics
75+
metricsListener net.Listener
76+
7377
mu sync.Mutex
7478
started bool
7579
errChan chan error

pkg/manager/manager.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package manager
1818

1919
import (
2020
"fmt"
21+
"net"
2122
"time"
2223

2324
"github.com/go-logr/logr"
@@ -33,6 +34,7 @@ import (
3334
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
3435
internalrecorder "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
3536
"sigs.k8s.io/controller-runtime/pkg/leaderelection"
37+
"sigs.k8s.io/controller-runtime/pkg/metrics"
3638
"sigs.k8s.io/controller-runtime/pkg/recorder"
3739
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3840
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
@@ -112,12 +114,17 @@ type Options struct {
112114
// For namespaced resources the cache will only hold objects from the desired namespace.
113115
Namespace string
114116

117+
// MetricsBindAddress is the TCP address that the controller should bind to
118+
// for serving prometheus metrics
119+
MetricsBindAddress string
120+
115121
// Dependency injection for testing
116122
newCache func(config *rest.Config, opts cache.Options) (cache.Cache, error)
117123
newClient func(config *rest.Config, options client.Options) (client.Client, error)
118124
newRecorderProvider func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger) (recorder.Provider, error)
119125
newResourceLock func(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error)
120126
newAdmissionDecoder func(scheme *runtime.Scheme) (types.Decoder, error)
127+
newMetricsListener func(addr string) (net.Listener, error)
121128
}
122129

123130
// Runnable allows a component to be started.
@@ -186,6 +193,13 @@ func New(config *rest.Config, options Options) (Manager, error) {
186193
return nil, err
187194
}
188195

196+
// Create the mertics listener. This will throw an error if the metrics bind
197+
// address is invalid or already in use.
198+
metricsListener, err := options.newMetricsListener(options.MetricsBindAddress)
199+
if err != nil {
200+
return nil, err
201+
}
202+
189203
return &controllerManager{
190204
config: config,
191205
scheme: options.Scheme,
@@ -204,6 +218,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
204218
recorderProvider: recorderProvider,
205219
resourceLock: resourceLock,
206220
mapper: mapper,
221+
metricsListener: metricsListener,
207222
}, nil
208223
}
209224

@@ -242,5 +257,9 @@ func setOptionsDefaults(options Options) Options {
242257
options.newAdmissionDecoder = admission.NewDecoder
243258
}
244259

260+
if options.newMetricsListener == nil {
261+
options.newMetricsListener = metrics.NewListener
262+
}
263+
245264
return options
246265
}

pkg/manager/manager_suite_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"k8s.io/client-go/kubernetes"
2525
"k8s.io/client-go/rest"
2626
"sigs.k8s.io/controller-runtime/pkg/envtest"
27+
"sigs.k8s.io/controller-runtime/pkg/metrics"
2728
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
2829
)
2930

@@ -48,9 +49,15 @@ var _ = BeforeSuite(func(done Done) {
4849
clientset, err = kubernetes.NewForConfig(cfg)
4950
Expect(err).NotTo(HaveOccurred())
5051

52+
// Prevent the metrics listener being created
53+
metrics.DefaultBindAddress = "0"
54+
5155
close(done)
5256
}, 60)
5357

5458
var _ = AfterSuite(func() {
5559
testenv.Stop()
60+
61+
// Put the DefaultBindAddress back
62+
metrics.DefaultBindAddress = ":8080"
5663
})

pkg/manager/manager_test.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package manager
1818

1919
import (
2020
"fmt"
21+
"net"
2122

2223
"github.com/go-logr/logr"
2324
. "github.com/onsi/ginkgo"
@@ -31,6 +32,7 @@ import (
3132
"sigs.k8s.io/controller-runtime/pkg/client"
3233
"sigs.k8s.io/controller-runtime/pkg/leaderelection"
3334
fakeleaderelection "sigs.k8s.io/controller-runtime/pkg/leaderelection/fake"
35+
"sigs.k8s.io/controller-runtime/pkg/metrics"
3436
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3537
"sigs.k8s.io/controller-runtime/pkg/recorder"
3638
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
@@ -69,7 +71,8 @@ var _ = Describe("manger.Manager", func() {
6971
m, err := New(cfg, Options{
7072
newClient: func(config *rest.Config, options client.Options) (client.Client, error) {
7173
return nil, fmt.Errorf("expected error")
72-
}})
74+
},
75+
})
7376
Expect(m).To(BeNil())
7477
Expect(err).To(HaveOccurred())
7578
Expect(err.Error()).To(ContainSubstring("expected error"))
@@ -81,7 +84,8 @@ var _ = Describe("manger.Manager", func() {
8184
m, err := New(cfg, Options{
8285
newCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) {
8386
return nil, fmt.Errorf("expected error")
84-
}})
87+
},
88+
})
8589
Expect(m).To(BeNil())
8690
Expect(err).To(HaveOccurred())
8791
Expect(err.Error()).To(ContainSubstring("expected error"))
@@ -92,7 +96,8 @@ var _ = Describe("manger.Manager", func() {
9296
m, err := New(cfg, Options{
9397
newRecorderProvider: func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger) (recorder.Provider, error) {
9498
return nil, fmt.Errorf("expected error")
95-
}})
99+
},
100+
})
96101
Expect(m).To(BeNil())
97102
Expect(err).To(HaveOccurred())
98103
Expect(err.Error()).To(ContainSubstring("expected error"))
@@ -123,6 +128,42 @@ var _ = Describe("manger.Manager", func() {
123128
Expect(err.Error()).To(ContainSubstring("unable to find leader election namespace: not running in-cluster, please specify LeaderElectionNamespace"))
124129
})
125130
})
131+
132+
It("should create a listener for the metrics if a valid address is provided", func() {
133+
var listener net.Listener
134+
m, err := New(cfg, Options{
135+
MetricsBindAddress: ":0",
136+
newMetricsListener: func(addr string) (net.Listener, error) {
137+
var err error
138+
listener, err = metrics.NewListener(addr)
139+
return listener, err
140+
},
141+
})
142+
Expect(m).ToNot(BeNil())
143+
Expect(err).ToNot(HaveOccurred())
144+
Expect(listener).ToNot(BeNil())
145+
Expect(listener.Close()).ToNot(HaveOccurred())
146+
})
147+
148+
It("should return an error if the metrics bind address is already in use", func() {
149+
ln, err := metrics.NewListener(":0")
150+
Expect(err).ShouldNot(HaveOccurred())
151+
152+
var listener net.Listener
153+
m, err := New(cfg, Options{
154+
MetricsBindAddress: ln.Addr().String(),
155+
newMetricsListener: func(addr string) (net.Listener, error) {
156+
var err error
157+
listener, err = metrics.NewListener(addr)
158+
return listener, err
159+
},
160+
})
161+
Expect(m).To(BeNil())
162+
Expect(err).To(HaveOccurred())
163+
Expect(listener).To(BeNil())
164+
165+
Expect(ln.Close()).ToNot(HaveOccurred())
166+
})
126167
})
127168

128169
Describe("Start", func() {

pkg/metrics/doc.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
/*
18+
Package metrics contains controller related metrics utilities
19+
*/
20+
package metrics

pkg/metrics/listener.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
"fmt"
21+
"net"
22+
)
23+
24+
// DefaultBindAddress sets the default bind address for the metrics
25+
// listener
26+
var DefaultBindAddress = ":8080"
27+
28+
// NewListener creates a new TCP listener bound to the given address.
29+
func NewListener(addr string) (net.Listener, error) {
30+
if addr == "" {
31+
// If the metrics bind address is empty, default to ":8080"
32+
addr = DefaultBindAddress
33+
}
34+
35+
// Add a case to disable metrics altogether
36+
if addr == "0" {
37+
return nil, nil
38+
}
39+
40+
ln, err := net.Listen("tcp", addr)
41+
if err != nil {
42+
return nil, fmt.Errorf("error listening on %s: %v", addr, err)
43+
}
44+
return ln, nil
45+
}

0 commit comments

Comments
 (0)