From 9bdc080a33a3399e20072c12f00bd42076118804 Mon Sep 17 00:00:00 2001 From: Stefan Bueringer Date: Tue, 20 Jul 2021 19:23:34 +0200 Subject: [PATCH] make it possible to monitor if the webhook server has been started MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stefan Büringer buringerst@vmware.com --- pkg/webhook/server.go | 30 ++++++++++++++++++++++++++++++ pkg/webhook/server_test.go | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/pkg/webhook/server.go b/pkg/webhook/server.go index 99b6ae9eb4..0895397fcb 100644 --- a/pkg/webhook/server.go +++ b/pkg/webhook/server.go @@ -28,10 +28,12 @@ import ( "path/filepath" "strconv" "sync" + "time" "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/metrics" ) @@ -87,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 } @@ -272,6 +278,9 @@ func (s *Server) Start(ctx context.Context) error { close(idleConnsClosed) }() + s.mu.Lock() + s.started = true + s.mu.Unlock() if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed { return err } @@ -280,6 +289,27 @@ func (s *Server) Start(ctx context.Context) 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 diff --git a/pkg/webhook/server_test.go b/pkg/webhook/server_test.go index ca7da4ce49..03323eede8 100644 --- a/pkg/webhook/server_test.go +++ b/pkg/webhook/server_test.go @@ -128,6 +128,8 @@ var _ = Describe("Webhook Server", func() { It("should serve a webhook on the requested path", func() { server.Register("/somepath", &testHandler{}) + Expect(server.StartedChecker()(nil)).ToNot(Succeed()) + doneCh := startServer() Eventually(func() ([]byte, error) { @@ -137,6 +139,8 @@ var _ = Describe("Webhook Server", func() { return ioutil.ReadAll(resp.Body) }).Should(Equal([]byte("gadzooks!"))) + Expect(server.StartedChecker()(nil)).To(Succeed()) + ctxCancel() Eventually(doneCh, "4s").Should(BeClosed()) })