Skip to content

Commit 20af901

Browse files
authored
Merge pull request #1163 from vincepri/watches-controller-bug
🐛 Controller.Watch() should not store watches if already started
2 parents d6829e9 + a6ec31f commit 20af901

File tree

2 files changed

+20
-11
lines changed

2 files changed

+20
-11
lines changed

pkg/internal/controller/controller.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ type Controller struct {
7474

7575
// TODO(community): Consider initializing a logger with the Controller Name as the tag
7676

77-
// watches maintains a list of sources, handlers, and predicates to start when the controller is started.
78-
watches []watchDescription
77+
// startWatches maintains a list of sources, handlers, and predicates to start when the controller is started.
78+
startWatches []watchDescription
7979

8080
// Log is used to log messages to users during reconciliation, or for example when a watch is started.
8181
Log logr.Logger
@@ -113,13 +113,16 @@ func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prc
113113
}
114114
}
115115

116-
c.watches = append(c.watches, watchDescription{src: src, handler: evthdler, predicates: prct})
117-
if c.Started {
118-
c.Log.Info("Starting EventSource", "source", src)
119-
return src.Start(evthdler, c.Queue, prct...)
116+
// Controller hasn't started yet, store the watches locally and return.
117+
//
118+
// These watches are going to be held on the controller struct until the manager or user calls Start(...).
119+
if !c.Started {
120+
c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
121+
return nil
120122
}
121123

122-
return nil
124+
c.Log.Info("Starting EventSource", "source", src)
125+
return src.Start(evthdler, c.Queue, prct...)
123126
}
124127

125128
// Start implements controller.Controller
@@ -143,7 +146,7 @@ func (c *Controller) Start(stop <-chan struct{}) error {
143146
// NB(directxman12): launch the sources *before* trying to wait for the
144147
// caches to sync so that they have a chance to register their intendeded
145148
// caches.
146-
for _, watch := range c.watches {
149+
for _, watch := range c.startWatches {
147150
c.Log.Info("Starting EventSource", "source", watch.src)
148151
if err := watch.src.Start(watch.handler, c.Queue, watch.predicates...); err != nil {
149152
return err
@@ -153,7 +156,7 @@ func (c *Controller) Start(stop <-chan struct{}) error {
153156
// Start the SharedIndexInformer factories to begin populating the SharedIndexInformer caches
154157
c.Log.Info("Starting Controller")
155158

156-
for _, watch := range c.watches {
159+
for _, watch := range c.startWatches {
157160
syncingSource, ok := watch.src.(source.SyncingSource)
158161
if !ok {
159162
continue
@@ -167,6 +170,12 @@ func (c *Controller) Start(stop <-chan struct{}) error {
167170
}
168171
}
169172

173+
// All the watches have been started, we can reset the local slice.
174+
//
175+
// We should never hold watches more than necessary, each watch source can hold a backing cache,
176+
// which won't be garbage collected if we hold a reference to it.
177+
c.startWatches = nil
178+
170179
if c.JitterPeriod == 0 {
171180
c.JitterPeriod = 1 * time.Second
172181
}

pkg/internal/controller/controller_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ var _ = Describe("controller", func() {
9191
Describe("Start", func() {
9292
It("should return an error if there is an error waiting for the informers", func(done Done) {
9393
f := false
94-
ctrl.watches = []watchDescription{{
94+
ctrl.startWatches = []watchDescription{{
9595
src: source.NewKindWithCache(&corev1.Pod{}, &informertest.FakeInformers{Synced: &f}),
9696
}}
9797
ctrl.Name = "foo"
@@ -115,7 +115,7 @@ var _ = Describe("controller", func() {
115115
Expect(err).NotTo(HaveOccurred())
116116
_, err = c.GetInformer(context.TODO(), &appsv1.ReplicaSet{})
117117
Expect(err).NotTo(HaveOccurred())
118-
ctrl.watches = []watchDescription{{
118+
ctrl.startWatches = []watchDescription{{
119119
src: source.NewKindWithCache(&appsv1.Deployment{}, &informertest.FakeInformers{}),
120120
}}
121121

0 commit comments

Comments
 (0)