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  }