knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/injection/health_check.go (about) 1 /* 2 Copyright 2023 The Knative 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 injection 18 19 import ( 20 "context" 21 "errors" 22 "net/http" 23 "strconv" 24 "time" 25 26 "knative.dev/pkg/logging" 27 ) 28 29 // HealthCheckDefaultPort defines the default port number for health probes 30 const HealthCheckDefaultPort = 8080 31 32 // ServeHealthProbes sets up liveness and readiness probes. 33 // If user sets no probes explicitly via the context then defaults are added. 34 func ServeHealthProbes(ctx context.Context, port int) error { 35 logger := logging.FromContext(ctx) 36 server := http.Server{ReadHeaderTimeout: time.Minute, Handler: muxWithHandles(ctx), Addr: ":" + strconv.Itoa(port)} 37 go func() { 38 <-ctx.Done() 39 _ = server.Shutdown(ctx) 40 }() 41 42 // start the web server on port and accept requests 43 logger.Infof("Probes server listening on port %d", port) 44 if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { 45 return err 46 } 47 return nil 48 } 49 50 func muxWithHandles(ctx context.Context) *http.ServeMux { 51 mux := http.NewServeMux() 52 readiness := getReadinessHandleOrDefault(ctx) 53 liveness := getLivenessHandleOrDefault(ctx) 54 mux.HandleFunc("/readiness", *readiness) 55 mux.HandleFunc("/health", *liveness) 56 return mux 57 } 58 59 func newDefaultProbesHandle(sigCtx context.Context) http.HandlerFunc { 60 logger := logging.FromContext(sigCtx) 61 return func(w http.ResponseWriter, r *http.Request) { 62 f := func() error { 63 select { 64 // When we get SIGTERM (sigCtx done), let readiness probes start failing. 65 case <-sigCtx.Done(): 66 logger.Info("Signal context canceled") 67 return errors.New("received SIGTERM from kubelet") 68 default: 69 return nil 70 } 71 } 72 if err := f(); err != nil { 73 logger.Errorf("Healthcheck failed: %v", err) 74 http.Error(w, err.Error(), http.StatusInternalServerError) 75 } else { 76 w.WriteHeader(http.StatusOK) 77 } 78 } 79 } 80 81 type addReadinessKey struct{} 82 83 // AddReadiness signals to probe setup logic to add a user provided probe handler 84 func AddReadiness(ctx context.Context, handlerFunc http.HandlerFunc) context.Context { 85 return context.WithValue(ctx, addReadinessKey{}, &handlerFunc) 86 } 87 88 func getReadinessHandleOrDefault(ctx context.Context) *http.HandlerFunc { 89 if ctx.Value(addReadinessKey{}) != nil { 90 return ctx.Value(addReadinessKey{}).(*http.HandlerFunc) 91 } 92 defaultHandle := newDefaultProbesHandle(ctx) 93 return &defaultHandle 94 } 95 96 type addLivenessKey struct{} 97 98 // AddLiveness signals to probe setup logic to add a user provided probe handler 99 func AddLiveness(ctx context.Context, handlerFunc http.HandlerFunc) context.Context { 100 return context.WithValue(ctx, addLivenessKey{}, &handlerFunc) 101 } 102 103 func getLivenessHandleOrDefault(ctx context.Context) *http.HandlerFunc { 104 if ctx.Value(addLivenessKey{}) != nil { 105 return ctx.Value(addLivenessKey{}).(*http.HandlerFunc) 106 } 107 defaultHandle := newDefaultProbesHandle(ctx) 108 return &defaultHandle 109 }