Skip to content

Commit 1000fac

Browse files
authored
Merge pull request #2337 from zqzten/basic_servers_runnable_group
🌱 Introduce a new runnable group for basic servers of the manager
2 parents 21779fb + ad70da2 commit 1000fac

File tree

3 files changed

+37
-42
lines changed

3 files changed

+37
-42
lines changed

pkg/manager/internal.go

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,9 @@ func (cm *controllerManager) addMetricsServer() error {
317317
})
318318
}
319319

320-
func (cm *controllerManager) serveHealthProbes() {
320+
func (cm *controllerManager) addHealthProbeServer() error {
321321
mux := http.NewServeMux()
322-
server := httpserver.New(mux)
322+
srv := httpserver.New(mux)
323323

324324
if cm.readyzHandler != nil {
325325
mux.Handle(cm.readinessEndpointName, http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
@@ -332,7 +332,12 @@ func (cm *controllerManager) serveHealthProbes() {
332332
mux.Handle(cm.livenessEndpointName+"/", http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
333333
}
334334

335-
go cm.httpServe("health probe", cm.logger, server, cm.healthProbeListener)
335+
return cm.add(&server{
336+
Kind: "health probe",
337+
Log: cm.logger,
338+
Server: srv,
339+
Listener: cm.healthProbeListener,
340+
})
336341
}
337342

338343
func (cm *controllerManager) addPprofServer() error {
@@ -353,42 +358,6 @@ func (cm *controllerManager) addPprofServer() error {
353358
})
354359
}
355360

356-
func (cm *controllerManager) httpServe(kind string, log logr.Logger, server *http.Server, ln net.Listener) {
357-
log = log.WithValues("kind", kind, "addr", ln.Addr())
358-
359-
go func() {
360-
log.Info("Starting server")
361-
if err := server.Serve(ln); err != nil {
362-
if errors.Is(err, http.ErrServerClosed) {
363-
return
364-
}
365-
if atomic.LoadInt64(cm.stopProcedureEngaged) > 0 {
366-
// There might be cases where connections are still open and we try to shutdown
367-
// but not having enough time to close the connection causes an error in Serve
368-
//
369-
// In that case we want to avoid returning an error to the main error channel.
370-
log.Error(err, "error on Serve after stop has been engaged")
371-
return
372-
}
373-
cm.errChan <- err
374-
}
375-
}()
376-
377-
// Shutdown the server when stop is closed.
378-
<-cm.internalProceduresStop
379-
if err := server.Shutdown(cm.shutdownCtx); err != nil {
380-
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
381-
// Avoid logging context related errors.
382-
return
383-
}
384-
if atomic.LoadInt64(cm.stopProcedureEngaged) > 0 {
385-
cm.logger.Error(err, "error on Shutdown after stop has been engaged")
386-
return
387-
}
388-
cm.errChan <- err
389-
}
390-
}
391-
392361
// Start starts the manager and waits indefinitely.
393362
// There is only two ways to have start return:
394363
// An error has occurred during in one of the internal operations,
@@ -449,7 +418,9 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
449418

450419
// Serve health probes.
451420
if cm.healthProbeListener != nil {
452-
cm.serveHealthProbes()
421+
if err := cm.addHealthProbeServer(); err != nil {
422+
return fmt.Errorf("failed to add health probe server: %w", err)
423+
}
453424
}
454425

455426
// Add pprof server
@@ -459,7 +430,17 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
459430
}
460431
}
461432

462-
// First start any webhook servers, which includes conversion, validation, and defaulting
433+
// First start any internal HTTP servers, which includes health probes, metrics and profiling if enabled.
434+
//
435+
// WARNING: Internal HTTP servers MUST start before any cache is populated, otherwise it would block
436+
// conversion webhooks to be ready for serving which make the cache never get ready.
437+
if err := cm.runnables.HTTPServers.Start(cm.internalCtx); err != nil {
438+
if err != nil {
439+
return fmt.Errorf("failed to start HTTP servers: %w", err)
440+
}
441+
}
442+
443+
// Start any webhook servers, which includes conversion, validation, and defaulting
463444
// webhooks that are registered.
464445
//
465446
// WARNING: Webhooks MUST start before any cache is populated, otherwise there is a race condition
@@ -591,10 +572,13 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
591572
cm.logger.Info("Stopping and waiting for caches")
592573
cm.runnables.Caches.StopAndWait(cm.shutdownCtx)
593574

594-
// Webhooks should come last, as they might be still serving some requests.
575+
// Webhooks and internal HTTP servers should come last, as they might be still serving some requests.
595576
cm.logger.Info("Stopping and waiting for webhooks")
596577
cm.runnables.Webhooks.StopAndWait(cm.shutdownCtx)
597578

579+
cm.logger.Info("Stopping and waiting for HTTP servers")
580+
cm.runnables.HTTPServers.StopAndWait(cm.shutdownCtx)
581+
598582
// Proceed to close the manager and overall shutdown context.
599583
cm.logger.Info("Wait completed, proceeding to shutdown the manager")
600584
shutdownCancel()

pkg/manager/runnable_group.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type runnableCheck func(ctx context.Context) bool
2828
// runnables handles all the runnables for a manager by grouping them accordingly to their
2929
// type (webhooks, caches etc.).
3030
type runnables struct {
31+
HTTPServers *runnableGroup
3132
Webhooks *runnableGroup
3233
Caches *runnableGroup
3334
LeaderElection *runnableGroup
@@ -37,6 +38,7 @@ type runnables struct {
3738
// newRunnables creates a new runnables object.
3839
func newRunnables(baseContext BaseContextFunc, errChan chan error) *runnables {
3940
return &runnables{
41+
HTTPServers: newRunnableGroup(baseContext, errChan),
4042
Webhooks: newRunnableGroup(baseContext, errChan),
4143
Caches: newRunnableGroup(baseContext, errChan),
4244
LeaderElection: newRunnableGroup(baseContext, errChan),
@@ -52,6 +54,8 @@ func newRunnables(baseContext BaseContextFunc, errChan chan error) *runnables {
5254
// The runnables added after Start are started directly.
5355
func (r *runnables) Add(fn Runnable) error {
5456
switch runnable := fn.(type) {
57+
case *server:
58+
return r.HTTPServers.Add(fn, nil)
5559
case hasCache:
5660
return r.Caches.Add(fn, func(ctx context.Context) bool {
5761
return runnable.GetCache().WaitForCacheSync(ctx)

pkg/manager/runnable_group_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ var _ = Describe("runnables", func() {
2121
Expect(newRunnables(defaultBaseContext, errCh)).ToNot(BeNil())
2222
})
2323

24+
It("should add HTTP servers to the appropriate group", func() {
25+
server := &server{}
26+
r := newRunnables(defaultBaseContext, errCh)
27+
Expect(r.Add(server)).To(Succeed())
28+
Expect(r.HTTPServers.startQueue).To(HaveLen(1))
29+
})
30+
2431
It("should add caches to the appropriate group", func() {
2532
cache := &cacheProvider{cache: &informertest.FakeInformers{Error: fmt.Errorf("expected error")}}
2633
r := newRunnables(defaultBaseContext, errCh)

0 commit comments

Comments
 (0)