Skip to content

Commit 667bb03

Browse files
committed
Add unit test that exercises controller warmup integration with manager.
1 parent 54f4fe3 commit 667bb03

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

pkg/internal/controller/controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ func (c *Controller[request]) Warmup(ctx context.Context) error {
177177
// WaitForWarmupComplete returns true if warmup has completed without error, and false if there was
178178
// an error during warmup. If context is cancelled, it returns true.
179179
func (c *Controller[request]) WaitForWarmupComplete(ctx context.Context) bool {
180+
if c.NeedWarmup == nil || !*c.NeedWarmup {
181+
return true
182+
}
180183
ticker := time.NewTicker(syncedPollPeriod)
181184
defer ticker.Stop()
182185

pkg/internal/controller/controller_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ import (
4040
"sigs.k8s.io/controller-runtime/pkg/client"
4141
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
4242
"sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue"
43+
"sigs.k8s.io/controller-runtime/pkg/envtest"
4344
"sigs.k8s.io/controller-runtime/pkg/event"
4445
"sigs.k8s.io/controller-runtime/pkg/handler"
4546
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics"
4647
"sigs.k8s.io/controller-runtime/pkg/internal/log"
48+
"sigs.k8s.io/controller-runtime/pkg/manager"
4749
"sigs.k8s.io/controller-runtime/pkg/reconcile"
4850
"sigs.k8s.io/controller-runtime/pkg/source"
4951
)
@@ -1089,6 +1091,77 @@ var _ = Describe("controller", func() {
10891091
cancel()
10901092
Expect(<-resultChan).To(BeTrue())
10911093
})
1094+
1095+
It("should be called before leader election runnables if warmup is enabled", func() {
1096+
// This unit test exists to ensure that a warmup enabled controller will actually be
1097+
// called in the warmup phase before the leader election runnables are started. It
1098+
// catches regressions in the controller that would not implement warmupRunnable from
1099+
// pkg/manager.
1100+
1101+
ctx, cancel := context.WithCancel(context.Background())
1102+
defer cancel()
1103+
1104+
hasCtrlWatchStarted, hasNonWarmupCtrlWatchStarted := atomic.Bool{}, atomic.Bool{}
1105+
1106+
// ctrl watch will block from finishing until the channel is produced to
1107+
ctrlWatchBlockingChan := make(chan struct{})
1108+
1109+
ctrl.CacheSyncTimeout = time.Second
1110+
ctrl.startWatches = []source.TypedSource[reconcile.Request]{
1111+
source.Func(func(ctx context.Context, _ workqueue.TypedRateLimitingInterface[reconcile.Request]) error {
1112+
hasCtrlWatchStarted.Store(true)
1113+
<-ctrlWatchBlockingChan
1114+
return nil
1115+
}),
1116+
}
1117+
1118+
nonWarmupCtrl := &Controller[reconcile.Request]{
1119+
MaxConcurrentReconciles: 1,
1120+
Do: fakeReconcile,
1121+
NewQueue: func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
1122+
return queue
1123+
},
1124+
LogConstructor: func(_ *reconcile.Request) logr.Logger {
1125+
return log.RuntimeLog.WithName("controller").WithName("test")
1126+
},
1127+
CacheSyncTimeout: time.Second,
1128+
NeedWarmup: ptr.To(false),
1129+
LeaderElected: ptr.To(true),
1130+
startWatches: []source.TypedSource[reconcile.Request]{
1131+
source.Func(func(ctx context.Context, _ workqueue.TypedRateLimitingInterface[reconcile.Request]) error {
1132+
hasNonWarmupCtrlWatchStarted.Store(true)
1133+
return nil
1134+
}),
1135+
},
1136+
}
1137+
1138+
By("Creating a manager")
1139+
testenv = &envtest.Environment{}
1140+
cfg, err := testenv.Start()
1141+
Expect(err).NotTo(HaveOccurred())
1142+
m, err := manager.New(cfg, manager.Options{
1143+
LeaderElection: false,
1144+
})
1145+
Expect(err).NotTo(HaveOccurred())
1146+
1147+
By("Adding warmup and non-warmup controllers to the manager")
1148+
Expect(m.Add(ctrl)).To(Succeed())
1149+
Expect(m.Add(nonWarmupCtrl)).To(Succeed())
1150+
1151+
By("Starting the manager")
1152+
go func() {
1153+
defer GinkgoRecover()
1154+
Expect(m.Start(ctx)).To(Succeed())
1155+
}()
1156+
1157+
By("Waiting for the warmup controller to start")
1158+
Eventually(hasCtrlWatchStarted.Load).Should(BeTrue())
1159+
Expect(hasNonWarmupCtrlWatchStarted.Load()).To(BeFalse())
1160+
1161+
By("Unblocking the warmup controller source start")
1162+
close(ctrlWatchBlockingChan)
1163+
Eventually(hasNonWarmupCtrlWatchStarted.Load).Should(BeTrue())
1164+
})
10921165
})
10931166

10941167
Describe("Warmup with warmup disabled", func() {

0 commit comments

Comments
 (0)