sigs.k8s.io/controller-runtime@v0.18.2/pkg/manager/manager.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package manager
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"net"
    24  	"net/http"
    25  	"time"
    26  
    27  	"github.com/go-logr/logr"
    28  	coordinationv1 "k8s.io/api/coordination/v1"
    29  	corev1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/meta"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/client-go/rest"
    33  	"k8s.io/client-go/tools/leaderelection/resourcelock"
    34  	"k8s.io/client-go/tools/record"
    35  	"k8s.io/utils/ptr"
    36  	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
    37  
    38  	"sigs.k8s.io/controller-runtime/pkg/cache"
    39  	"sigs.k8s.io/controller-runtime/pkg/client"
    40  	"sigs.k8s.io/controller-runtime/pkg/cluster"
    41  	"sigs.k8s.io/controller-runtime/pkg/config"
    42  	"sigs.k8s.io/controller-runtime/pkg/healthz"
    43  	intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
    44  	"sigs.k8s.io/controller-runtime/pkg/leaderelection"
    45  	"sigs.k8s.io/controller-runtime/pkg/log"
    46  	"sigs.k8s.io/controller-runtime/pkg/recorder"
    47  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    48  )
    49  
    50  // Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables.
    51  // A Manager is required to create Controllers.
    52  type Manager interface {
    53  	// Cluster holds a variety of methods to interact with a cluster.
    54  	cluster.Cluster
    55  
    56  	// Add will set requested dependencies on the component, and cause the component to be
    57  	// started when Start is called.
    58  	// Depending on if a Runnable implements LeaderElectionRunnable interface, a Runnable can be run in either
    59  	// non-leaderelection mode (always running) or leader election mode (managed by leader election if enabled).
    60  	Add(Runnable) error
    61  
    62  	// Elected is closed when this manager is elected leader of a group of
    63  	// managers, either because it won a leader election or because no leader
    64  	// election was configured.
    65  	Elected() <-chan struct{}
    66  
    67  	// AddMetricsServerExtraHandler adds an extra handler served on path to the http server that serves metrics.
    68  	// Might be useful to register some diagnostic endpoints e.g. pprof.
    69  	//
    70  	// Note that these endpoints are meant to be sensitive and shouldn't be exposed publicly.
    71  	//
    72  	// If the simple path -> handler mapping offered here is not enough,
    73  	// a new http server/listener should be added as Runnable to the manager via Add method.
    74  	AddMetricsServerExtraHandler(path string, handler http.Handler) error
    75  
    76  	// AddHealthzCheck allows you to add Healthz checker
    77  	AddHealthzCheck(name string, check healthz.Checker) error
    78  
    79  	// AddReadyzCheck allows you to add Readyz checker
    80  	AddReadyzCheck(name string, check healthz.Checker) error
    81  
    82  	// Start starts all registered Controllers and blocks until the context is cancelled.
    83  	// Returns an error if there is an error starting any controller.
    84  	//
    85  	// If LeaderElection is used, the binary must be exited immediately after this returns,
    86  	// otherwise components that need leader election might continue to run after the leader
    87  	// lock was lost.
    88  	Start(ctx context.Context) error
    89  
    90  	// GetWebhookServer returns a webhook.Server
    91  	GetWebhookServer() webhook.Server
    92  
    93  	// GetLogger returns this manager's logger.
    94  	GetLogger() logr.Logger
    95  
    96  	// GetControllerOptions returns controller global configuration options.
    97  	GetControllerOptions() config.Controller
    98  }
    99  
   100  // Options are the arguments for creating a new Manager.
   101  type Options struct {
   102  	// Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources.
   103  	// Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better
   104  	// to pass your own scheme in. See the documentation in pkg/scheme for more information.
   105  	//
   106  	// If set, the Scheme will be used to create the default Client and Cache.
   107  	Scheme *runtime.Scheme
   108  
   109  	// MapperProvider provides the rest mapper used to map go types to Kubernetes APIs.
   110  	//
   111  	// If set, the RESTMapper returned by this function is used to create the RESTMapper
   112  	// used by the Client and Cache.
   113  	MapperProvider func(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error)
   114  
   115  	// Cache is the cache.Options that will be used to create the default Cache.
   116  	// By default, the cache will watch and list requested objects in all namespaces.
   117  	Cache cache.Options
   118  
   119  	// NewCache is the function that will create the cache to be used
   120  	// by the manager. If not set this will use the default new cache function.
   121  	//
   122  	// When using a custom NewCache, the Cache options will be passed to the
   123  	// NewCache function.
   124  	//
   125  	// NOTE: LOW LEVEL PRIMITIVE!
   126  	// Only use a custom NewCache if you know what you are doing.
   127  	NewCache cache.NewCacheFunc
   128  
   129  	// Client is the client.Options that will be used to create the default Client.
   130  	// By default, the client will use the cache for reads and direct calls for writes.
   131  	Client client.Options
   132  
   133  	// NewClient is the func that creates the client to be used by the manager.
   134  	// If not set this will create a Client backed by a Cache for read operations
   135  	// and a direct Client for write operations.
   136  	//
   137  	// When using a custom NewClient, the Client options will be passed to the
   138  	// NewClient function.
   139  	//
   140  	// NOTE: LOW LEVEL PRIMITIVE!
   141  	// Only use a custom NewClient if you know what you are doing.
   142  	NewClient client.NewClientFunc
   143  
   144  	// Logger is the logger that should be used by this manager.
   145  	// If none is set, it defaults to log.Log global logger.
   146  	Logger logr.Logger
   147  
   148  	// LeaderElection determines whether or not to use leader election when
   149  	// starting the manager.
   150  	LeaderElection bool
   151  
   152  	// LeaderElectionResourceLock determines which resource lock to use for leader election,
   153  	// defaults to "leases". Change this value only if you know what you are doing.
   154  	//
   155  	// If you are using `configmaps`/`endpoints` resource lock and want to migrate to "leases",
   156  	// you might do so by migrating to the respective multilock first ("configmapsleases" or "endpointsleases"),
   157  	// which will acquire a leader lock on both resources.
   158  	// After all your users have migrated to the multilock, you can go ahead and migrate to "leases".
   159  	// Please also keep in mind, that users might skip versions of your controller.
   160  	//
   161  	// Note: before controller-runtime version v0.7, it was set to "configmaps".
   162  	// And from v0.7 to v0.11, the default was "configmapsleases", which was
   163  	// used to migrate from configmaps to leases.
   164  	// Since the default was "configmapsleases" for over a year, spanning five minor releases,
   165  	// any actively maintained operators are very likely to have a released version that uses
   166  	// "configmapsleases". Therefore defaulting to "leases" should be safe since v0.12.
   167  	//
   168  	// So, what do you have to do when you are updating your controller-runtime dependency
   169  	// from a lower version to v0.12 or newer?
   170  	// - If your operator matches at least one of these conditions:
   171  	//   - the LeaderElectionResourceLock in your operator has already been explicitly set to "leases"
   172  	//   - the old controller-runtime version is between v0.7.0 and v0.11.x and the
   173  	//     LeaderElectionResourceLock wasn't set or was set to "leases"/"configmapsleases"/"endpointsleases"
   174  	//   feel free to update controller-runtime to v0.12 or newer.
   175  	// - Otherwise, you may have to take these steps:
   176  	//   1. update controller-runtime to v0.12 or newer in your go.mod
   177  	//   2. set LeaderElectionResourceLock to "configmapsleases" (or "endpointsleases")
   178  	//   3. package your operator and upgrade it in all your clusters
   179  	//   4. only if you have finished 3, you can remove the LeaderElectionResourceLock to use the default "leases"
   180  	// Otherwise, your operator might end up with multiple running instances that
   181  	// each acquired leadership through different resource locks during upgrades and thus
   182  	// act on the same resources concurrently.
   183  	LeaderElectionResourceLock string
   184  
   185  	// LeaderElectionNamespace determines the namespace in which the leader
   186  	// election resource will be created.
   187  	LeaderElectionNamespace string
   188  
   189  	// LeaderElectionID determines the name of the resource that leader election
   190  	// will use for holding the leader lock.
   191  	LeaderElectionID string
   192  
   193  	// LeaderElectionConfig can be specified to override the default configuration
   194  	// that is used to build the leader election client.
   195  	LeaderElectionConfig *rest.Config
   196  
   197  	// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
   198  	// when the Manager ends. This requires the binary to immediately end when the
   199  	// Manager is stopped, otherwise this setting is unsafe. Setting this significantly
   200  	// speeds up voluntary leader transitions as the new leader doesn't have to wait
   201  	// LeaseDuration time first.
   202  	LeaderElectionReleaseOnCancel bool
   203  
   204  	// LeaderElectionResourceLockInterface allows to provide a custom resourcelock.Interface that was created outside
   205  	// of the controller-runtime. If this value is set the options LeaderElectionID, LeaderElectionNamespace,
   206  	// LeaderElectionResourceLock, LeaseDuration, RenewDeadline and RetryPeriod will be ignored. This can be useful if you
   207  	// want to use a locking mechanism that is currently not supported, like a MultiLock across two Kubernetes clusters.
   208  	LeaderElectionResourceLockInterface resourcelock.Interface
   209  
   210  	// LeaseDuration is the duration that non-leader candidates will
   211  	// wait to force acquire leadership. This is measured against time of
   212  	// last observed ack. Default is 15 seconds.
   213  	LeaseDuration *time.Duration
   214  
   215  	// RenewDeadline is the duration that the acting controlplane will retry
   216  	// refreshing leadership before giving up. Default is 10 seconds.
   217  	RenewDeadline *time.Duration
   218  
   219  	// RetryPeriod is the duration the LeaderElector clients should wait
   220  	// between tries of actions. Default is 2 seconds.
   221  	RetryPeriod *time.Duration
   222  
   223  	// Metrics are the metricsserver.Options that will be used to create the metricsserver.Server.
   224  	Metrics metricsserver.Options
   225  
   226  	// HealthProbeBindAddress is the TCP address that the controller should bind to
   227  	// for serving health probes
   228  	// It can be set to "0" or "" to disable serving the health probe.
   229  	HealthProbeBindAddress string
   230  
   231  	// Readiness probe endpoint name, defaults to "readyz"
   232  	ReadinessEndpointName string
   233  
   234  	// Liveness probe endpoint name, defaults to "healthz"
   235  	LivenessEndpointName string
   236  
   237  	// PprofBindAddress is the TCP address that the controller should bind to
   238  	// for serving pprof.
   239  	// It can be set to "" or "0" to disable the pprof serving.
   240  	// Since pprof may contain sensitive information, make sure to protect it
   241  	// before exposing it to public.
   242  	PprofBindAddress string
   243  
   244  	// WebhookServer is an externally configured webhook.Server. By default,
   245  	// a Manager will create a server via webhook.NewServer with default settings.
   246  	// If this is set, the Manager will use this server instead.
   247  	WebhookServer webhook.Server
   248  
   249  	// BaseContext is the function that provides Context values to Runnables
   250  	// managed by the Manager. If a BaseContext function isn't provided, Runnables
   251  	// will receive a new Background Context instead.
   252  	BaseContext BaseContextFunc
   253  
   254  	// EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
   255  	// Use this to customize the event correlator and spam filter
   256  	//
   257  	// Deprecated: using this may cause goroutine leaks if the lifetime of your manager or controllers
   258  	// is shorter than the lifetime of your process.
   259  	EventBroadcaster record.EventBroadcaster
   260  
   261  	// GracefulShutdownTimeout is the duration given to runnable to stop before the manager actually returns on stop.
   262  	// To disable graceful shutdown, set to time.Duration(0)
   263  	// To use graceful shutdown without timeout, set to a negative duration, e.G. time.Duration(-1)
   264  	// The graceful shutdown is skipped for safety reasons in case the leader election lease is lost.
   265  	GracefulShutdownTimeout *time.Duration
   266  
   267  	// Controller contains global configuration options for controllers
   268  	// registered within this manager.
   269  	// +optional
   270  	Controller config.Controller
   271  
   272  	// makeBroadcaster allows deferring the creation of the broadcaster to
   273  	// avoid leaking goroutines if we never call Start on this manager.  It also
   274  	// returns whether or not this is a "owned" broadcaster, and as such should be
   275  	// stopped with the manager.
   276  	makeBroadcaster intrec.EventBroadcasterProducer
   277  
   278  	// Dependency injection for testing
   279  	newRecorderProvider    func(config *rest.Config, httpClient *http.Client, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
   280  	newResourceLock        func(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error)
   281  	newMetricsServer       func(options metricsserver.Options, config *rest.Config, httpClient *http.Client) (metricsserver.Server, error)
   282  	newHealthProbeListener func(addr string) (net.Listener, error)
   283  	newPprofListener       func(addr string) (net.Listener, error)
   284  }
   285  
   286  // BaseContextFunc is a function used to provide a base Context to Runnables
   287  // managed by a Manager.
   288  type BaseContextFunc func() context.Context
   289  
   290  // Runnable allows a component to be started.
   291  // It's very important that Start blocks until
   292  // it's done running.
   293  type Runnable interface {
   294  	// Start starts running the component.  The component will stop running
   295  	// when the context is closed. Start blocks until the context is closed or
   296  	// an error occurs.
   297  	Start(context.Context) error
   298  }
   299  
   300  // RunnableFunc implements Runnable using a function.
   301  // It's very important that the given function block
   302  // until it's done running.
   303  type RunnableFunc func(context.Context) error
   304  
   305  // Start implements Runnable.
   306  func (r RunnableFunc) Start(ctx context.Context) error {
   307  	return r(ctx)
   308  }
   309  
   310  // LeaderElectionRunnable knows if a Runnable needs to be run in the leader election mode.
   311  type LeaderElectionRunnable interface {
   312  	// NeedLeaderElection returns true if the Runnable needs to be run in the leader election mode.
   313  	// e.g. controllers need to be run in leader election mode, while webhook server doesn't.
   314  	NeedLeaderElection() bool
   315  }
   316  
   317  // New returns a new Manager for creating Controllers.
   318  // Note that if ContentType in the given config is not set, "application/vnd.kubernetes.protobuf"
   319  // will be used for all built-in resources of Kubernetes, and "application/json" is for other types
   320  // including all CRD resources.
   321  func New(config *rest.Config, options Options) (Manager, error) {
   322  	if config == nil {
   323  		return nil, errors.New("must specify Config")
   324  	}
   325  	// Set default values for options fields
   326  	options = setOptionsDefaults(options)
   327  
   328  	cluster, err := cluster.New(config, func(clusterOptions *cluster.Options) {
   329  		clusterOptions.Scheme = options.Scheme
   330  		clusterOptions.MapperProvider = options.MapperProvider
   331  		clusterOptions.Logger = options.Logger
   332  		clusterOptions.NewCache = options.NewCache
   333  		clusterOptions.NewClient = options.NewClient
   334  		clusterOptions.Cache = options.Cache
   335  		clusterOptions.Client = options.Client
   336  		clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck
   337  	})
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  
   342  	config = rest.CopyConfig(config)
   343  	if config.UserAgent == "" {
   344  		config.UserAgent = rest.DefaultKubernetesUserAgent()
   345  	}
   346  
   347  	// Create the recorder provider to inject event recorders for the components.
   348  	// TODO(directxman12): the log for the event provider should have a context (name, tags, etc) specific
   349  	// to the particular controller that it's being injected into, rather than a generic one like is here.
   350  	recorderProvider, err := options.newRecorderProvider(config, cluster.GetHTTPClient(), cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster)
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	// Create the resource lock to enable leader election)
   356  	var leaderConfig *rest.Config
   357  	var leaderRecorderProvider *intrec.Provider
   358  
   359  	if options.LeaderElectionConfig == nil {
   360  		leaderConfig = rest.CopyConfig(config)
   361  		leaderRecorderProvider = recorderProvider
   362  	} else {
   363  		leaderConfig = rest.CopyConfig(options.LeaderElectionConfig)
   364  		scheme := cluster.GetScheme()
   365  		err := corev1.AddToScheme(scheme)
   366  		if err != nil {
   367  			return nil, err
   368  		}
   369  		err = coordinationv1.AddToScheme(scheme)
   370  		if err != nil {
   371  			return nil, err
   372  		}
   373  		httpClient, err := rest.HTTPClientFor(options.LeaderElectionConfig)
   374  		if err != nil {
   375  			return nil, err
   376  		}
   377  		leaderRecorderProvider, err = options.newRecorderProvider(leaderConfig, httpClient, scheme, options.Logger.WithName("events"), options.makeBroadcaster)
   378  		if err != nil {
   379  			return nil, err
   380  		}
   381  	}
   382  
   383  	var resourceLock resourcelock.Interface
   384  	if options.LeaderElectionResourceLockInterface != nil && options.LeaderElection {
   385  		resourceLock = options.LeaderElectionResourceLockInterface
   386  	} else {
   387  		resourceLock, err = options.newResourceLock(leaderConfig, leaderRecorderProvider, leaderelection.Options{
   388  			LeaderElection:             options.LeaderElection,
   389  			LeaderElectionResourceLock: options.LeaderElectionResourceLock,
   390  			LeaderElectionID:           options.LeaderElectionID,
   391  			LeaderElectionNamespace:    options.LeaderElectionNamespace,
   392  		})
   393  		if err != nil {
   394  			return nil, err
   395  		}
   396  	}
   397  
   398  	// Create the metrics server.
   399  	metricsServer, err := options.newMetricsServer(options.Metrics, config, cluster.GetHTTPClient())
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	// Create health probes listener. This will throw an error if the bind
   405  	// address is invalid or already in use.
   406  	healthProbeListener, err := options.newHealthProbeListener(options.HealthProbeBindAddress)
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  
   411  	// Create pprof listener. This will throw an error if the bind
   412  	// address is invalid or already in use.
   413  	pprofListener, err := options.newPprofListener(options.PprofBindAddress)
   414  	if err != nil {
   415  		return nil, fmt.Errorf("failed to new pprof listener: %w", err)
   416  	}
   417  
   418  	errChan := make(chan error, 1)
   419  	runnables := newRunnables(options.BaseContext, errChan)
   420  	return &controllerManager{
   421  		stopProcedureEngaged:          ptr.To(int64(0)),
   422  		cluster:                       cluster,
   423  		runnables:                     runnables,
   424  		errChan:                       errChan,
   425  		recorderProvider:              recorderProvider,
   426  		resourceLock:                  resourceLock,
   427  		metricsServer:                 metricsServer,
   428  		controllerConfig:              options.Controller,
   429  		logger:                        options.Logger,
   430  		elected:                       make(chan struct{}),
   431  		webhookServer:                 options.WebhookServer,
   432  		leaderElectionID:              options.LeaderElectionID,
   433  		leaseDuration:                 *options.LeaseDuration,
   434  		renewDeadline:                 *options.RenewDeadline,
   435  		retryPeriod:                   *options.RetryPeriod,
   436  		healthProbeListener:           healthProbeListener,
   437  		readinessEndpointName:         options.ReadinessEndpointName,
   438  		livenessEndpointName:          options.LivenessEndpointName,
   439  		pprofListener:                 pprofListener,
   440  		gracefulShutdownTimeout:       *options.GracefulShutdownTimeout,
   441  		internalProceduresStop:        make(chan struct{}),
   442  		leaderElectionStopped:         make(chan struct{}),
   443  		leaderElectionReleaseOnCancel: options.LeaderElectionReleaseOnCancel,
   444  	}, nil
   445  }
   446  
   447  // defaultHealthProbeListener creates the default health probes listener bound to the given address.
   448  func defaultHealthProbeListener(addr string) (net.Listener, error) {
   449  	if addr == "" || addr == "0" {
   450  		return nil, nil
   451  	}
   452  
   453  	ln, err := net.Listen("tcp", addr)
   454  	if err != nil {
   455  		return nil, fmt.Errorf("error listening on %s: %w", addr, err)
   456  	}
   457  	return ln, nil
   458  }
   459  
   460  // defaultPprofListener creates the default pprof listener bound to the given address.
   461  func defaultPprofListener(addr string) (net.Listener, error) {
   462  	if addr == "" || addr == "0" {
   463  		return nil, nil
   464  	}
   465  
   466  	ln, err := net.Listen("tcp", addr)
   467  	if err != nil {
   468  		return nil, fmt.Errorf("error listening on %s: %w", addr, err)
   469  	}
   470  	return ln, nil
   471  }
   472  
   473  // defaultBaseContext is used as the BaseContext value in Options if one
   474  // has not already been set.
   475  func defaultBaseContext() context.Context {
   476  	return context.Background()
   477  }
   478  
   479  // setOptionsDefaults set default values for Options fields.
   480  func setOptionsDefaults(options Options) Options {
   481  	// Allow newResourceLock to be mocked
   482  	if options.newResourceLock == nil {
   483  		options.newResourceLock = leaderelection.NewResourceLock
   484  	}
   485  
   486  	// Allow newRecorderProvider to be mocked
   487  	if options.newRecorderProvider == nil {
   488  		options.newRecorderProvider = intrec.NewProvider
   489  	}
   490  
   491  	// This is duplicated with pkg/cluster, we need it here
   492  	// for the leader election and there to provide the user with
   493  	// an EventBroadcaster
   494  	if options.EventBroadcaster == nil {
   495  		// defer initialization to avoid leaking by default
   496  		options.makeBroadcaster = func() (record.EventBroadcaster, bool) {
   497  			return record.NewBroadcaster(), true
   498  		}
   499  	} else {
   500  		options.makeBroadcaster = func() (record.EventBroadcaster, bool) {
   501  			return options.EventBroadcaster, false
   502  		}
   503  	}
   504  
   505  	if options.newMetricsServer == nil {
   506  		options.newMetricsServer = metricsserver.NewServer
   507  	}
   508  	leaseDuration, renewDeadline, retryPeriod := defaultLeaseDuration, defaultRenewDeadline, defaultRetryPeriod
   509  	if options.LeaseDuration == nil {
   510  		options.LeaseDuration = &leaseDuration
   511  	}
   512  
   513  	if options.RenewDeadline == nil {
   514  		options.RenewDeadline = &renewDeadline
   515  	}
   516  
   517  	if options.RetryPeriod == nil {
   518  		options.RetryPeriod = &retryPeriod
   519  	}
   520  
   521  	if options.ReadinessEndpointName == "" {
   522  		options.ReadinessEndpointName = defaultReadinessEndpoint
   523  	}
   524  
   525  	if options.LivenessEndpointName == "" {
   526  		options.LivenessEndpointName = defaultLivenessEndpoint
   527  	}
   528  
   529  	if options.newHealthProbeListener == nil {
   530  		options.newHealthProbeListener = defaultHealthProbeListener
   531  	}
   532  
   533  	if options.newPprofListener == nil {
   534  		options.newPprofListener = defaultPprofListener
   535  	}
   536  
   537  	if options.GracefulShutdownTimeout == nil {
   538  		gracefulShutdownTimeout := defaultGracefulShutdownPeriod
   539  		options.GracefulShutdownTimeout = &gracefulShutdownTimeout
   540  	}
   541  
   542  	if options.Logger.GetSink() == nil {
   543  		options.Logger = log.Log
   544  	}
   545  
   546  	if options.BaseContext == nil {
   547  		options.BaseContext = defaultBaseContext
   548  	}
   549  
   550  	if options.WebhookServer == nil {
   551  		options.WebhookServer = webhook.NewServer(webhook.Options{})
   552  	}
   553  
   554  	return options
   555  }