Skip to content

Commit 010fba4

Browse files
committed
Switch to using goleaks to check for leaks
This switches to using the goleaks package to check for leaks, which should give us a more complete picture of the particular goroutine that's leaking, and should avoid issues where we leak a goroutine, but also stop an old one. This also force-closes keep-alive connections in the leak tests, since those look like leaks, but will actually time out after 30s (outside the timescope of the test).
1 parent 7e9b011 commit 010fba4

File tree

6 files changed

+45
-23
lines changed

6 files changed

+45
-23
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ require (
1616
github.com/prometheus/client_golang v1.7.1
1717
github.com/prometheus/client_model v0.2.0
1818
github.com/spf13/pflag v1.0.5
19+
go.uber.org/goleak v1.1.10
1920
go.uber.org/zap v1.15.0
2021
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
2122
gomodules.xyz/jsonpatch/v2 v2.1.0

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
253253
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
254254
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
255255
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
256+
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
256257
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
257258
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
258259
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -372,6 +373,8 @@ go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
372373
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
373374
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
374375
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
376+
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
377+
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
375378
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
376379
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
377380
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
@@ -519,6 +522,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
519522
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
520523
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
521524
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
525+
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
522526
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
523527
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
524528
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

pkg/controller/controller_suite_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package controller_test
1818

1919
import (
20+
"net/http"
2021
"testing"
2122

2223
. "github.com/onsi/ginkgo"
@@ -45,6 +46,9 @@ var testenv *envtest.Environment
4546
var cfg *rest.Config
4647
var clientset *kubernetes.Clientset
4748

49+
// clientTransport is used to force-close keep-alives in tests that check for leaks
50+
var clientTransport *http.Transport
51+
4852
var _ = BeforeSuite(func(done Done) {
4953
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
5054

@@ -64,6 +68,9 @@ var _ = BeforeSuite(func(done Done) {
6468
cfg, err = testenv.Start()
6569
Expect(err).NotTo(HaveOccurred())
6670

71+
clientTransport = &http.Transport{}
72+
cfg.Transport = clientTransport
73+
6774
clientset, err = kubernetes.NewForConfig(cfg)
6875
Expect(err).NotTo(HaveOccurred())
6976

pkg/controller/controller_test.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ package controller_test
1818

1919
import (
2020
"fmt"
21-
"os"
22-
rt "runtime"
23-
"runtime/pprof"
2421

2522
. "github.com/onsi/ginkgo"
2623
. "github.com/onsi/gomega"
24+
"go.uber.org/goleak"
2725

2826
"sigs.k8s.io/controller-runtime/pkg/client"
2927
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -96,35 +94,38 @@ var _ = Describe("controller.Controller", func() {
9694
})
9795

9896
It("should not leak goroutines when stopped", func() {
99-
// NB(directxman12): this test was flaky before on CI, but my guess
100-
// is that the flakiness was caused by an expect on the count.
101-
// Eventually should fix it, but if not, consider disabling again.
97+
currentGRs := goleak.IgnoreCurrent()
98+
10299
m, err := manager.New(cfg, manager.Options{})
103100
Expect(err).NotTo(HaveOccurred())
104101

105102
_, err = controller.New("new-controller", m, controller.Options{Reconciler: rec})
106103
Expect(err).NotTo(HaveOccurred())
107104

108-
startGoroutines := rt.NumGoroutine()
109105
s := make(chan struct{})
110106
close(s)
111107

112-
Expect(m.Start(s)).NotTo(HaveOccurred())
113-
Eventually(rt.NumGoroutine /* pass the function, don't call it */).Should(Equal(startGoroutines))
108+
Expect(m.Start(s)).To(Succeed())
109+
110+
// force-close keep-alive connections. These'll time anyway (after
111+
// like 30s or so) but force it to speed up the tests.
112+
clientTransport.CloseIdleConnections()
113+
Eventually(func() error { return goleak.Find(currentGRs) }).Should(Succeed())
114114
})
115115

116116
It("should not create goroutines if never started", func() {
117-
startGoroutines := rt.NumGoroutine()
118-
Expect(pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)).To(Succeed())
117+
currentGRs := goleak.IgnoreCurrent()
119118

120119
m, err := manager.New(cfg, manager.Options{})
121120
Expect(err).NotTo(HaveOccurred())
122121

123122
_, err = controller.New("new-controller", m, controller.Options{Reconciler: rec})
124123
Expect(err).NotTo(HaveOccurred())
125124

126-
Expect(pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)).To(Succeed())
127-
Eventually(rt.NumGoroutine /* pass func, don't call */).Should(Equal(startGoroutines))
125+
// force-close keep-alive connections. These'll time anyway (after
126+
// like 30s or so) but force it to speed up the tests.
127+
clientTransport.CloseIdleConnections()
128+
Eventually(func() error { return goleak.Find(currentGRs) }).Should(Succeed())
128129
})
129130
})
130131
})

pkg/manager/manager_suite_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package manager
1818

1919
import (
20+
"net/http"
2021
"testing"
2122

2223
. "github.com/onsi/ginkgo"
@@ -40,6 +41,9 @@ var testenv *envtest.Environment
4041
var cfg *rest.Config
4142
var clientset *kubernetes.Clientset
4243

44+
// clientTransport is used to force-close keep-alives in tests that check for leaks
45+
var clientTransport *http.Transport
46+
4347
var _ = BeforeSuite(func(done Done) {
4448
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
4549

@@ -49,6 +53,9 @@ var _ = BeforeSuite(func(done Done) {
4953
cfg, err = testenv.Start()
5054
Expect(err).NotTo(HaveOccurred())
5155

56+
clientTransport = &http.Transport{}
57+
cfg.Transport = clientTransport
58+
5259
clientset, err = kubernetes.NewForConfig(cfg)
5360
Expect(err).NotTo(HaveOccurred())
5461

pkg/manager/manager_test.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"net"
2525
"net/http"
2626
"path"
27-
rt "runtime"
2827
"strings"
2928
"sync"
3029
"time"
@@ -33,6 +32,7 @@ import (
3332
. "github.com/onsi/ginkgo"
3433
. "github.com/onsi/gomega"
3534
"github.com/prometheus/client_golang/prometheus"
35+
"go.uber.org/goleak"
3636
corev1 "k8s.io/api/core/v1"
3737
"k8s.io/apimachinery/pkg/api/meta"
3838
"k8s.io/apimachinery/pkg/runtime"
@@ -1204,25 +1204,24 @@ var _ = Describe("manger.Manager", func() {
12041204
})
12051205
})
12061206

1207-
// This test has been marked as pending because it has been causing lots of flakes in CI.
1208-
// It should be rewritten at some point later in the future.
12091207
It("should not leak goroutines when stopped", func() {
1210-
// NB(directxman12): this test was flaky before on CI, but my guess
1211-
// is that the flakiness was caused by an expect on the count.
1212-
// Eventually should fix it, but if not, consider disabling again.
1208+
currentGRs := goleak.IgnoreCurrent()
1209+
12131210
m, err := New(cfg, Options{})
12141211
Expect(err).NotTo(HaveOccurred())
1215-
startGoroutines := rt.NumGoroutine()
12161212

12171213
s := make(chan struct{})
12181214
close(s)
12191215
Expect(m.Start(s)).NotTo(HaveOccurred())
12201216

1221-
Eventually(rt.NumGoroutine /* pass the function, don't call it */).Should(Equal(startGoroutines))
1217+
// force-close keep-alive connections. These'll time anyway (after
1218+
// like 30s or so) but force it to speed up the tests.
1219+
clientTransport.CloseIdleConnections()
1220+
Eventually(func() error { return goleak.Find(currentGRs) }).Should(Succeed())
12221221
})
12231222

12241223
It("should not leak goroutines if the default event broadcaster is used & events are emitted", func() {
1225-
startGoroutines := rt.NumGoroutine()
1224+
currentGRs := goleak.IgnoreCurrent()
12261225

12271226
m, err := New(cfg, Options{ /* implicit: default setting for EventBroadcaster */ })
12281227
Expect(err).NotTo(HaveOccurred())
@@ -1261,7 +1260,10 @@ var _ = Describe("manger.Manager", func() {
12611260
close(stopCh)
12621261
<-doneCh
12631262

1264-
Eventually(rt.NumGoroutine /* pass the function, don't call it */).Should(Equal(startGoroutines))
1263+
// force-close keep-alive connections. These'll time anyway (after
1264+
// like 30s or so) but force it to speed up the tests.
1265+
clientTransport.CloseIdleConnections()
1266+
Eventually(func() error { return goleak.Find(currentGRs) }).Should(Succeed())
12651267
})
12661268

12671269
It("should provide a function to get the Config", func() {

0 commit comments

Comments
 (0)