324
vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
generated
vendored
324
vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go
generated
vendored
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/leaderelection"
|
||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
@@ -37,10 +37,11 @@ import (
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cluster"
|
||||
"sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
"sigs.k8s.io/controller-runtime/pkg/recorder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
)
|
||||
@@ -52,50 +53,35 @@ const (
|
||||
defaultRetryPeriod = 2 * time.Second
|
||||
defaultGracefulShutdownPeriod = 30 * time.Second
|
||||
|
||||
defaultReadinessEndpoint = "/readyz/"
|
||||
defaultLivenessEndpoint = "/healthz/"
|
||||
defaultReadinessEndpoint = "/readyz"
|
||||
defaultLivenessEndpoint = "/healthz"
|
||||
defaultMetricsEndpoint = "/metrics"
|
||||
)
|
||||
|
||||
var log = logf.RuntimeLog.WithName("manager")
|
||||
var _ Runnable = &controllerManager{}
|
||||
|
||||
type controllerManager struct {
|
||||
// config is the rest.config used to talk to the apiserver. Required.
|
||||
config *rest.Config
|
||||
|
||||
// scheme is the scheme injected into Controllers, EventHandlers, Sources and Predicates. Defaults
|
||||
// to scheme.scheme.
|
||||
scheme *runtime.Scheme
|
||||
// cluster holds a variety of methods to interact with a cluster. Required.
|
||||
cluster cluster.Cluster
|
||||
|
||||
// leaderElectionRunnables is the set of Controllers that the controllerManager injects deps into and Starts.
|
||||
// These Runnables are managed by lead election.
|
||||
leaderElectionRunnables []Runnable
|
||||
|
||||
// nonLeaderElectionRunnables is the set of webhook servers that the controllerManager injects deps into and Starts.
|
||||
// These Runnables will not be blocked by lead election.
|
||||
nonLeaderElectionRunnables []Runnable
|
||||
|
||||
cache cache.Cache
|
||||
|
||||
// TODO(directxman12): Provide an escape hatch to get individual indexers
|
||||
// client is the client injected into Controllers (and EventHandlers, Sources and Predicates).
|
||||
client client.Client
|
||||
|
||||
// apiReader is the reader that will make requests to the api server and not the cache.
|
||||
apiReader client.Reader
|
||||
|
||||
// fieldIndexes knows how to add field indexes over the Cache used by this controller,
|
||||
// which can later be consumed via field selectors from the injected client.
|
||||
fieldIndexes client.FieldIndexer
|
||||
|
||||
// recorderProvider is used to generate event recorders that will be injected into Controllers
|
||||
// (and EventHandlers, Sources and Predicates).
|
||||
recorderProvider recorder.Provider
|
||||
recorderProvider *intrec.Provider
|
||||
|
||||
// resourceLock forms the basis for leader election
|
||||
resourceLock resourcelock.Interface
|
||||
|
||||
// mapper is used to map resources to kind, and map kind and version.
|
||||
mapper meta.RESTMapper
|
||||
// leaderElectionReleaseOnCancel defines if the manager should step back from the leader lease
|
||||
// on shutdown
|
||||
leaderElectionReleaseOnCancel bool
|
||||
|
||||
// metricsListener is used to serve prometheus metrics
|
||||
metricsListener net.Listener
|
||||
@@ -124,15 +110,8 @@ type controllerManager struct {
|
||||
healthzStarted bool
|
||||
errChan chan error
|
||||
|
||||
// internalStop is the stop channel *actually* used by everything involved
|
||||
// with the manager as a stop channel, so that we can pass a stop channel
|
||||
// to things that need it off the bat (like the Channel source). It can
|
||||
// be closed via `internalStopper` (by being the same underlying channel).
|
||||
internalStop <-chan struct{}
|
||||
|
||||
// internalStopper is the write side of the internal stop channel, allowing us to close it.
|
||||
// It and `internalStop` should point to the same channel.
|
||||
internalStopper chan<- struct{}
|
||||
// controllerOptions are the global controller options.
|
||||
controllerOptions v1alpha1.ControllerConfigurationSpec
|
||||
|
||||
// Logger is the logger that should be used by this manager.
|
||||
// If none is set, it defaults to log.Log global logger.
|
||||
@@ -143,6 +122,10 @@ type controllerManager struct {
|
||||
// it must be deferred until after gracefulShutdown is done.
|
||||
leaderElectionCancel context.CancelFunc
|
||||
|
||||
// leaderElectionStopped is an internal channel used to signal the stopping procedure that the
|
||||
// LeaderElection.Run(...) function has returned and the shutdown can proceed.
|
||||
leaderElectionStopped chan struct{}
|
||||
|
||||
// stop procedure engaged. In other words, we should not add anything else to the manager
|
||||
stopProcedureEngaged bool
|
||||
|
||||
@@ -151,7 +134,7 @@ type controllerManager struct {
|
||||
// election was configured.
|
||||
elected chan struct{}
|
||||
|
||||
startCache func(stop <-chan struct{}) error
|
||||
caches []hasCache
|
||||
|
||||
// port is the port that the webhook server serves at.
|
||||
port int
|
||||
@@ -163,6 +146,9 @@ type controllerManager struct {
|
||||
certDir string
|
||||
|
||||
webhookServer *webhook.Server
|
||||
// webhookServerOnce will be called in GetWebhookServer() to optionally initialize
|
||||
// webhookServer if unset, and Add() it to controllerManager.
|
||||
webhookServerOnce sync.Once
|
||||
|
||||
// leaseDuration is the duration that non-leader candidates will
|
||||
// wait to force acquire leadership.
|
||||
@@ -190,6 +176,18 @@ type controllerManager struct {
|
||||
// after the gracefulShutdownTimeout ended. It must not be accessed before internalStop
|
||||
// is closed because it will be nil.
|
||||
shutdownCtx context.Context
|
||||
|
||||
internalCtx context.Context
|
||||
internalCancel context.CancelFunc
|
||||
|
||||
// internalProceduresStop channel is used internally to the manager when coordinating
|
||||
// the proper shutdown of servers. This channel is also used for dependency injection.
|
||||
internalProceduresStop chan struct{}
|
||||
}
|
||||
|
||||
type hasCache interface {
|
||||
Runnable
|
||||
GetCache() cache.Cache
|
||||
}
|
||||
|
||||
// Add sets dependencies on i, and adds it to the list of Runnables to start.
|
||||
@@ -211,6 +209,8 @@ func (cm *controllerManager) Add(r Runnable) error {
|
||||
if leRunnable, ok := r.(LeaderElectionRunnable); ok && !leRunnable.NeedLeaderElection() {
|
||||
shouldStart = cm.started
|
||||
cm.nonLeaderElectionRunnables = append(cm.nonLeaderElectionRunnables, r)
|
||||
} else if hasCache, ok := r.(hasCache); ok {
|
||||
cm.caches = append(cm.caches, hasCache)
|
||||
} else {
|
||||
shouldStart = cm.startedLeader
|
||||
cm.leaderElectionRunnables = append(cm.leaderElectionRunnables, r)
|
||||
@@ -224,34 +224,21 @@ func (cm *controllerManager) Add(r Runnable) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: use the equivalent Options field to set a field. This method will be removed in v0.10.
|
||||
func (cm *controllerManager) SetFields(i interface{}) error {
|
||||
if _, err := inject.ConfigInto(cm.config, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.ClientInto(cm.client, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.APIReaderInto(cm.apiReader, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.SchemeInto(cm.scheme, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.CacheInto(cm.cache, i); err != nil {
|
||||
if err := cm.cluster.SetFields(i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.InjectorInto(cm.SetFields, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.StopChannelInto(cm.internalStop, i); err != nil {
|
||||
if _, err := inject.StopChannelInto(cm.internalProceduresStop, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.MapperInto(cm.mapper, i); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := inject.LoggerInto(log, i); err != nil {
|
||||
if _, err := inject.LoggerInto(cm.logger, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -264,17 +251,16 @@ func (cm *controllerManager) AddMetricsExtraHandler(path string, handler http.Ha
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
_, found := cm.metricsExtraHandlers[path]
|
||||
if found {
|
||||
if _, found := cm.metricsExtraHandlers[path]; found {
|
||||
return fmt.Errorf("can't register extra handler by duplicate path %q on metrics http server", path)
|
||||
}
|
||||
|
||||
cm.metricsExtraHandlers[path] = handler
|
||||
log.V(2).Info("Registering metrics http server extra handler", "path", path)
|
||||
cm.logger.V(2).Info("Registering metrics http server extra handler", "path", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddHealthzCheck allows you to add Healthz checker
|
||||
// AddHealthzCheck allows you to add Healthz checker.
|
||||
func (cm *controllerManager) AddHealthzCheck(name string, check healthz.Checker) error {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
@@ -295,7 +281,7 @@ func (cm *controllerManager) AddHealthzCheck(name string, check healthz.Checker)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddReadyzCheck allows you to add Readyz checker
|
||||
// AddReadyzCheck allows you to add Readyz checker.
|
||||
func (cm *controllerManager) AddReadyzCheck(name string, check healthz.Checker) error {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
@@ -317,71 +303,62 @@ func (cm *controllerManager) AddReadyzCheck(name string, check healthz.Checker)
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetConfig() *rest.Config {
|
||||
return cm.config
|
||||
return cm.cluster.GetConfig()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetClient() client.Client {
|
||||
return cm.client
|
||||
return cm.cluster.GetClient()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetScheme() *runtime.Scheme {
|
||||
return cm.scheme
|
||||
return cm.cluster.GetScheme()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetFieldIndexer() client.FieldIndexer {
|
||||
return cm.fieldIndexes
|
||||
return cm.cluster.GetFieldIndexer()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetCache() cache.Cache {
|
||||
return cm.cache
|
||||
return cm.cluster.GetCache()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetEventRecorderFor(name string) record.EventRecorder {
|
||||
return cm.recorderProvider.GetEventRecorderFor(name)
|
||||
return cm.cluster.GetEventRecorderFor(name)
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetRESTMapper() meta.RESTMapper {
|
||||
return cm.mapper
|
||||
return cm.cluster.GetRESTMapper()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetAPIReader() client.Reader {
|
||||
return cm.apiReader
|
||||
return cm.cluster.GetAPIReader()
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetWebhookServer() *webhook.Server {
|
||||
server, wasNew := func() (*webhook.Server, bool) {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
if cm.webhookServer != nil {
|
||||
return cm.webhookServer, false
|
||||
cm.webhookServerOnce.Do(func() {
|
||||
if cm.webhookServer == nil {
|
||||
cm.webhookServer = &webhook.Server{
|
||||
Port: cm.port,
|
||||
Host: cm.host,
|
||||
CertDir: cm.certDir,
|
||||
}
|
||||
}
|
||||
|
||||
cm.webhookServer = &webhook.Server{
|
||||
Port: cm.port,
|
||||
Host: cm.host,
|
||||
CertDir: cm.certDir,
|
||||
}
|
||||
return cm.webhookServer, true
|
||||
}()
|
||||
|
||||
// only add the server if *we ourselves* just registered it.
|
||||
// Add has its own lock, so just do this separately -- there shouldn't
|
||||
// be a "race" in this lock gap because the condition is the population
|
||||
// of cm.webhookServer, not anything to do with Add.
|
||||
if wasNew {
|
||||
if err := cm.Add(server); err != nil {
|
||||
if err := cm.Add(cm.webhookServer); err != nil {
|
||||
panic("unable to add webhook server to the controller manager")
|
||||
}
|
||||
}
|
||||
return server
|
||||
})
|
||||
return cm.webhookServer
|
||||
}
|
||||
|
||||
func (cm *controllerManager) GetLogger() logr.Logger {
|
||||
return cm.logger
|
||||
}
|
||||
|
||||
func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
|
||||
func (cm *controllerManager) GetControllerOptions() v1alpha1.ControllerConfigurationSpec {
|
||||
return cm.controllerOptions
|
||||
}
|
||||
|
||||
func (cm *controllerManager) serveMetrics() {
|
||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{
|
||||
ErrorHandling: promhttp.HTTPErrorOnError,
|
||||
})
|
||||
@@ -402,8 +379,8 @@ func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
|
||||
Handler: mux,
|
||||
}
|
||||
// Run the server
|
||||
cm.startRunnable(RunnableFunc(func(stop <-chan struct{}) error {
|
||||
log.Info("starting metrics server", "path", defaultMetricsEndpoint)
|
||||
cm.startRunnable(RunnableFunc(func(_ context.Context) error {
|
||||
cm.logger.Info("starting metrics server", "path", defaultMetricsEndpoint)
|
||||
if err := server.Serve(cm.metricsListener); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
@@ -411,50 +388,60 @@ func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
|
||||
}))
|
||||
|
||||
// Shutdown the server when stop is closed
|
||||
<-stop
|
||||
<-cm.internalProceduresStop
|
||||
if err := server.Shutdown(cm.shutdownCtx); err != nil {
|
||||
cm.errChan <- err
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *controllerManager) serveHealthProbes(stop <-chan struct{}) {
|
||||
// TODO(hypnoglow): refactor locking to use anonymous func in the similar way
|
||||
// it's done in serveMetrics.
|
||||
cm.mu.Lock()
|
||||
func (cm *controllerManager) serveHealthProbes() {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
if cm.readyzHandler != nil {
|
||||
mux.Handle(cm.readinessEndpointName, http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
|
||||
}
|
||||
if cm.healthzHandler != nil {
|
||||
mux.Handle(cm.livenessEndpointName, http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
|
||||
}
|
||||
|
||||
server := http.Server{
|
||||
Handler: mux,
|
||||
}
|
||||
// Run server
|
||||
cm.startRunnable(RunnableFunc(func(stop <-chan struct{}) error {
|
||||
if err := server.Serve(cm.healthProbeListener); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
|
||||
func() {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
if cm.readyzHandler != nil {
|
||||
mux.Handle(cm.readinessEndpointName, http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
|
||||
// Append '/' suffix to handle subpaths
|
||||
mux.Handle(cm.readinessEndpointName+"/", http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
cm.healthzStarted = true
|
||||
cm.mu.Unlock()
|
||||
if cm.healthzHandler != nil {
|
||||
mux.Handle(cm.livenessEndpointName, http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
|
||||
// Append '/' suffix to handle subpaths
|
||||
mux.Handle(cm.livenessEndpointName+"/", http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
|
||||
}
|
||||
|
||||
// Run server
|
||||
cm.startRunnable(RunnableFunc(func(_ context.Context) error {
|
||||
if err := server.Serve(cm.healthProbeListener); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
cm.healthzStarted = true
|
||||
}()
|
||||
|
||||
// Shutdown the server when stop is closed
|
||||
<-stop
|
||||
<-cm.internalProceduresStop
|
||||
if err := server.Shutdown(cm.shutdownCtx); err != nil {
|
||||
cm.errChan <- err
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *controllerManager) Start(stop <-chan struct{}) (err error) {
|
||||
func (cm *controllerManager) Start(ctx context.Context) (err error) {
|
||||
if err := cm.Add(cm.cluster); err != nil {
|
||||
return fmt.Errorf("failed to add cluster to runnables: %w", err)
|
||||
}
|
||||
cm.internalCtx, cm.internalCancel = context.WithCancel(ctx)
|
||||
|
||||
// This chan indicates that stop is complete, in other words all runnables have returned or timeout on stop request
|
||||
stopComplete := make(chan struct{})
|
||||
defer close(stopComplete)
|
||||
// This must be deferred after closing stopComplete, otherwise we deadlock
|
||||
// This must be deferred after closing stopComplete, otherwise we deadlock.
|
||||
defer func() {
|
||||
// https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/gettyimages-459889618-1533579787.jpg
|
||||
stopErr := cm.engageStopProcedure(stopComplete)
|
||||
@@ -463,7 +450,7 @@ func (cm *controllerManager) Start(stop <-chan struct{}) (err error) {
|
||||
// Utilerrors.Aggregate allows to use errors.Is for all contained errors
|
||||
// whereas fmt.Errorf allows wrapping at most one error which means the
|
||||
// other one can not be found anymore.
|
||||
err = utilerrors.NewAggregate([]error{err, stopErr})
|
||||
err = kerrors.NewAggregate([]error{err, stopErr})
|
||||
} else {
|
||||
err = stopErr
|
||||
}
|
||||
@@ -481,12 +468,12 @@ func (cm *controllerManager) Start(stop <-chan struct{}) (err error) {
|
||||
// (If we don't serve metrics for non-leaders, prometheus will still scrape
|
||||
// the pod but will get a connection refused)
|
||||
if cm.metricsListener != nil {
|
||||
go cm.serveMetrics(cm.internalStop)
|
||||
go cm.serveMetrics()
|
||||
}
|
||||
|
||||
// Serve health probes
|
||||
if cm.healthProbeListener != nil {
|
||||
go cm.serveHealthProbes(cm.internalStop)
|
||||
go cm.serveHealthProbes()
|
||||
}
|
||||
|
||||
go cm.startNonLeaderElectionRunnables()
|
||||
@@ -499,13 +486,13 @@ func (cm *controllerManager) Start(stop <-chan struct{}) (err error) {
|
||||
}
|
||||
} else {
|
||||
// Treat not having leader election enabled the same as being elected.
|
||||
cm.startLeaderElectionRunnables()
|
||||
close(cm.elected)
|
||||
go cm.startLeaderElectionRunnables()
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-stop:
|
||||
case <-ctx.Done():
|
||||
// We are done
|
||||
return nil
|
||||
case err := <-cm.errChan:
|
||||
@@ -516,15 +503,20 @@ func (cm *controllerManager) Start(stop <-chan struct{}) (err error) {
|
||||
|
||||
// engageStopProcedure signals all runnables to stop, reads potential errors
|
||||
// from the errChan and waits for them to end. It must not be called more than once.
|
||||
func (cm *controllerManager) engageStopProcedure(stopComplete chan struct{}) error {
|
||||
var cancel context.CancelFunc
|
||||
func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) error {
|
||||
// Populate the shutdown context.
|
||||
var shutdownCancel context.CancelFunc
|
||||
if cm.gracefulShutdownTimeout > 0 {
|
||||
cm.shutdownCtx, cancel = context.WithTimeout(context.Background(), cm.gracefulShutdownTimeout)
|
||||
cm.shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(), cm.gracefulShutdownTimeout)
|
||||
} else {
|
||||
cm.shutdownCtx, cancel = context.WithCancel(context.Background())
|
||||
cm.shutdownCtx, shutdownCancel = context.WithCancel(context.Background())
|
||||
}
|
||||
defer cancel()
|
||||
close(cm.internalStopper)
|
||||
defer shutdownCancel()
|
||||
|
||||
// Cancel the internal stop channel and wait for the procedures to stop and complete.
|
||||
close(cm.internalProceduresStop)
|
||||
cm.internalCancel()
|
||||
|
||||
// Start draining the errors before acquiring the lock to make sure we don't deadlock
|
||||
// if something that has the lock is blocked on trying to write into the unbuffered
|
||||
// channel after something else already wrote into it.
|
||||
@@ -533,7 +525,7 @@ func (cm *controllerManager) engageStopProcedure(stopComplete chan struct{}) err
|
||||
select {
|
||||
case err, ok := <-cm.errChan:
|
||||
if ok {
|
||||
log.Error(err, "error received after stop sequence was engaged")
|
||||
cm.logger.Error(err, "error received after stop sequence was engaged")
|
||||
}
|
||||
case <-stopComplete:
|
||||
return
|
||||
@@ -546,28 +538,36 @@ func (cm *controllerManager) engageStopProcedure(stopComplete chan struct{}) err
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
cm.stopProcedureEngaged = true
|
||||
return cm.waitForRunnableToEnd(cm.shutdownCtx, cancel)
|
||||
|
||||
// we want to close this after the other runnables stop, because we don't
|
||||
// want things like leader election to try and emit events on a closed
|
||||
// channel
|
||||
defer cm.recorderProvider.Stop(cm.shutdownCtx)
|
||||
return cm.waitForRunnableToEnd(shutdownCancel)
|
||||
}
|
||||
|
||||
// waitForRunnableToEnd blocks until all runnables ended or the
|
||||
// tearDownTimeout was reached. In the latter case, an error is returned.
|
||||
func (cm *controllerManager) waitForRunnableToEnd(ctx context.Context, cancel context.CancelFunc) error {
|
||||
defer cancel()
|
||||
|
||||
func (cm *controllerManager) waitForRunnableToEnd(shutdownCancel context.CancelFunc) (retErr error) {
|
||||
// Cancel leader election only after we waited. It will os.Exit() the app for safety.
|
||||
defer func() {
|
||||
if cm.leaderElectionCancel != nil {
|
||||
if retErr == nil && cm.leaderElectionCancel != nil {
|
||||
// After asking the context to be cancelled, make sure
|
||||
// we wait for the leader stopped channel to be closed, otherwise
|
||||
// we might encounter race conditions between this code
|
||||
// and the event recorder, which is used within leader election code.
|
||||
cm.leaderElectionCancel()
|
||||
<-cm.leaderElectionStopped
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
cm.waitForRunnable.Wait()
|
||||
cancel()
|
||||
shutdownCancel()
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
if err := ctx.Err(); err != nil && err != context.Canceled {
|
||||
<-cm.shutdownCtx.Done()
|
||||
if err := cm.shutdownCtx.Err(); err != nil && err != context.Canceled {
|
||||
return fmt.Errorf("failed waiting for all runnables to end within grace period of %s: %w", cm.gracefulShutdownTimeout, err)
|
||||
}
|
||||
return nil
|
||||
@@ -577,10 +577,27 @@ func (cm *controllerManager) startNonLeaderElectionRunnables() {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
cm.waitForCache()
|
||||
// First start any webhook servers, which includes conversion, validation, and defaulting
|
||||
// webhooks that are registered.
|
||||
//
|
||||
// WARNING: Webhooks MUST start before any cache is populated, otherwise there is a race condition
|
||||
// between conversion webhooks and the cache sync (usually initial list) which causes the webhooks
|
||||
// to never start because no cache can be populated.
|
||||
for _, c := range cm.nonLeaderElectionRunnables {
|
||||
if _, ok := c.(*webhook.Server); ok {
|
||||
cm.startRunnable(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Start and wait for caches.
|
||||
cm.waitForCache(cm.internalCtx)
|
||||
|
||||
// Start the non-leaderelection Runnables after the cache has synced
|
||||
for _, c := range cm.nonLeaderElectionRunnables {
|
||||
if _, ok := c.(*webhook.Server); ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Controllers block, but we want to return an error if any have an error starting.
|
||||
// Write any Start errors to a channel so we can return them
|
||||
cm.startRunnable(c)
|
||||
@@ -591,7 +608,7 @@ func (cm *controllerManager) startLeaderElectionRunnables() {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
cm.waitForCache()
|
||||
cm.waitForCache(cm.internalCtx)
|
||||
|
||||
// Start the leader election Runnables after the cache has synced
|
||||
for _, c := range cm.leaderElectionRunnables {
|
||||
@@ -603,22 +620,20 @@ func (cm *controllerManager) startLeaderElectionRunnables() {
|
||||
cm.startedLeader = true
|
||||
}
|
||||
|
||||
func (cm *controllerManager) waitForCache() {
|
||||
func (cm *controllerManager) waitForCache(ctx context.Context) {
|
||||
if cm.started {
|
||||
return
|
||||
}
|
||||
|
||||
// Start the Cache. Allow the function to start the cache to be mocked out for testing
|
||||
if cm.startCache == nil {
|
||||
cm.startCache = cm.cache.Start
|
||||
for _, cache := range cm.caches {
|
||||
cm.startRunnable(cache)
|
||||
}
|
||||
cm.startRunnable(RunnableFunc(func(stop <-chan struct{}) error {
|
||||
return cm.startCache(stop)
|
||||
}))
|
||||
|
||||
// Wait for the caches to sync.
|
||||
// TODO(community): Check the return value and write a test
|
||||
cm.cache.WaitForCacheSync(cm.internalStop)
|
||||
for _, cache := range cm.caches {
|
||||
cache.GetCache().WaitForCacheSync(ctx)
|
||||
}
|
||||
// TODO: This should be the return value of cm.cache.WaitForCacheSync but we abuse
|
||||
// cm.started as check if we already started the cache so it must always become true.
|
||||
// Making sure that the cache doesn't get started twice is needed to not get a "close
|
||||
@@ -650,18 +665,23 @@ func (cm *controllerManager) startLeaderElection() (err error) {
|
||||
RetryPeriod: cm.retryPeriod,
|
||||
Callbacks: leaderelection.LeaderCallbacks{
|
||||
OnStartedLeading: func(_ context.Context) {
|
||||
close(cm.elected)
|
||||
cm.startLeaderElectionRunnables()
|
||||
close(cm.elected)
|
||||
},
|
||||
OnStoppedLeading: cm.onStoppedLeading,
|
||||
},
|
||||
ReleaseOnCancel: cm.leaderElectionReleaseOnCancel,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the leader elector process
|
||||
go l.Run(ctx)
|
||||
go func() {
|
||||
l.Run(ctx)
|
||||
<-ctx.Done()
|
||||
close(cm.leaderElectionStopped)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -673,7 +693,7 @@ func (cm *controllerManager) startRunnable(r Runnable) {
|
||||
cm.waitForRunnable.Add(1)
|
||||
go func() {
|
||||
defer cm.waitForRunnable.Done()
|
||||
if err := r.Start(cm.internalStop); err != nil {
|
||||
if err := r.Start(cm.internalCtx); err != nil {
|
||||
cm.errChan <- err
|
||||
}
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user