125
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
generated
vendored
125
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
generated
vendored
@@ -28,26 +28,34 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/certwatcher"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
|
||||
)
|
||||
|
||||
// DefaultPort is the default port that the webhook server serves.
|
||||
var DefaultPort = 443
|
||||
var DefaultPort = 9443
|
||||
|
||||
// Server is an admission webhook server that can serve traffic and
|
||||
// generates related k8s resources for deploying.
|
||||
//
|
||||
// TLS is required for a webhook to be accessed by kubernetes, so
|
||||
// you must provide a CertName and KeyName or have valid cert/key
|
||||
// at the default locations (tls.crt and tls.key). If you do not
|
||||
// want to configure TLS (i.e for testing purposes) run an
|
||||
// admission.StandaloneWebhook in your own server.
|
||||
type Server struct {
|
||||
// Host is the address that the server will listen on.
|
||||
// Defaults to "" - all addresses.
|
||||
Host string
|
||||
|
||||
// Port is the port number that the server will serve.
|
||||
// It will be defaulted to 443 if unspecified.
|
||||
// It will be defaulted to 9443 if unspecified.
|
||||
Port int
|
||||
|
||||
// CertDir is the directory that contains the server key and certificate. The
|
||||
@@ -64,6 +72,10 @@ type Server struct {
|
||||
// Defaults to "", which means server does not verify client's certificate.
|
||||
ClientCAName string
|
||||
|
||||
// TLSVersion is the minimum version of TLS supported. Accepts
|
||||
// "", "1.0", "1.1", "1.2" and "1.3" only ("" is equivalent to "1.0" for backwards compatibility)
|
||||
TLSMinVersion string
|
||||
|
||||
// WebhookMux is the multiplexer that handles different webhooks.
|
||||
WebhookMux *http.ServeMux
|
||||
|
||||
@@ -77,6 +89,10 @@ type Server struct {
|
||||
// defaultingOnce ensures that the default fields are only ever set once.
|
||||
defaultingOnce sync.Once
|
||||
|
||||
// started is set to true immediately before the server is started
|
||||
// and thus can be used to check if the server has been started
|
||||
started bool
|
||||
|
||||
// mu protects access to the webhook map & setFields for Start, Register, etc
|
||||
mu sync.Mutex
|
||||
}
|
||||
@@ -118,13 +134,12 @@ func (s *Server) Register(path string, hook http.Handler) {
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.defaultingOnce.Do(s.setDefaults)
|
||||
_, found := s.webhooks[path]
|
||||
if found {
|
||||
if _, found := s.webhooks[path]; found {
|
||||
panic(fmt.Errorf("can't register duplicate path: %v", path))
|
||||
}
|
||||
// TODO(directxman12): call setfields if we've already started the server
|
||||
s.webhooks[path] = hook
|
||||
s.WebhookMux.Handle(path, instrumentedHook(path, hook))
|
||||
s.WebhookMux.Handle(path, metrics.InstrumentedHook(path, hook))
|
||||
|
||||
regLog := log.WithValues("path", path)
|
||||
regLog.Info("registering webhook")
|
||||
@@ -149,30 +164,49 @@ func (s *Server) Register(path string, hook http.Handler) {
|
||||
}
|
||||
}
|
||||
|
||||
// instrumentedHook adds some instrumentation on top of the given webhook.
|
||||
func instrumentedHook(path string, hookRaw http.Handler) http.Handler {
|
||||
lbl := prometheus.Labels{"webhook": path}
|
||||
// StartStandalone runs a webhook server without
|
||||
// a controller manager.
|
||||
func (s *Server) StartStandalone(ctx context.Context, scheme *runtime.Scheme) error {
|
||||
// Use the Kubernetes client-go scheme if none is specified
|
||||
if scheme == nil {
|
||||
scheme = kscheme.Scheme
|
||||
}
|
||||
|
||||
lat := metrics.RequestLatency.MustCurryWith(lbl)
|
||||
cnt := metrics.RequestTotal.MustCurryWith(lbl)
|
||||
gge := metrics.RequestInFlight.With(lbl)
|
||||
if err := s.InjectFunc(func(i interface{}) error {
|
||||
if _, err := inject.SchemeInto(scheme, i); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize the most likely HTTP status codes.
|
||||
cnt.WithLabelValues("200")
|
||||
cnt.WithLabelValues("500")
|
||||
return s.Start(ctx)
|
||||
}
|
||||
|
||||
return promhttp.InstrumentHandlerDuration(
|
||||
lat,
|
||||
promhttp.InstrumentHandlerCounter(
|
||||
cnt,
|
||||
promhttp.InstrumentHandlerInFlight(gge, hookRaw),
|
||||
),
|
||||
)
|
||||
// tlsVersion converts from human-readable TLS version (for example "1.1")
|
||||
// to the values accepted by tls.Config (for example 0x301).
|
||||
func tlsVersion(version string) (uint16, error) {
|
||||
switch version {
|
||||
// default is previous behaviour
|
||||
case "":
|
||||
return tls.VersionTLS10, nil
|
||||
case "1.0":
|
||||
return tls.VersionTLS10, nil
|
||||
case "1.1":
|
||||
return tls.VersionTLS11, nil
|
||||
case "1.2":
|
||||
return tls.VersionTLS12, nil
|
||||
case "1.3":
|
||||
return tls.VersionTLS13, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid TLSMinVersion %v: expects 1.0, 1.1, 1.2, 1.3 or empty", version)
|
||||
}
|
||||
}
|
||||
|
||||
// Start runs the server.
|
||||
// It will install the webhook related resources depend on the server configuration.
|
||||
func (s *Server) Start(stop <-chan struct{}) error {
|
||||
func (s *Server) Start(ctx context.Context) error {
|
||||
s.defaultingOnce.Do(s.setDefaults)
|
||||
|
||||
baseHookLog := log.WithName("webhooks")
|
||||
@@ -187,14 +221,20 @@ func (s *Server) Start(stop <-chan struct{}) error {
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := certWatcher.Start(stop); err != nil {
|
||||
if err := certWatcher.Start(ctx); err != nil {
|
||||
log.Error(err, "certificate watcher error")
|
||||
}
|
||||
}()
|
||||
|
||||
cfg := &tls.Config{
|
||||
tlsMinVersion, err := tlsVersion(s.TLSMinVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := &tls.Config{ //nolint:gosec
|
||||
NextProtos: []string{"h2"},
|
||||
GetCertificate: certWatcher.GetCertificate,
|
||||
MinVersion: tlsMinVersion,
|
||||
}
|
||||
|
||||
// load CA to verify client certificate
|
||||
@@ -214,7 +254,7 @@ func (s *Server) Start(stop <-chan struct{}) error {
|
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
listener, err := tls.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(int(s.Port))), cfg)
|
||||
listener, err := tls.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -227,7 +267,7 @@ func (s *Server) Start(stop <-chan struct{}) error {
|
||||
|
||||
idleConnsClosed := make(chan struct{})
|
||||
go func() {
|
||||
<-stop
|
||||
<-ctx.Done()
|
||||
log.Info("shutting down webhook server")
|
||||
|
||||
// TODO: use a context with reasonable timeout
|
||||
@@ -238,8 +278,10 @@ func (s *Server) Start(stop <-chan struct{}) error {
|
||||
close(idleConnsClosed)
|
||||
}()
|
||||
|
||||
err = srv.Serve(listener)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
s.mu.Lock()
|
||||
s.started = true
|
||||
s.mu.Unlock()
|
||||
if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -247,6 +289,27 @@ func (s *Server) Start(stop <-chan struct{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartedChecker returns an healthz.Checker which is healthy after the
|
||||
// server has been started.
|
||||
func (s *Server) StartedChecker() healthz.Checker {
|
||||
return func(req *http.Request) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if !s.started {
|
||||
return fmt.Errorf("webhook server has not been started yet")
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)), 10*time.Second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook server is not reachable: %v", err)
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// InjectFunc injects the field setter into the server.
|
||||
func (s *Server) InjectFunc(f inject.Func) error {
|
||||
s.setFields = f
|
||||
|
||||
Reference in New Issue
Block a user