github.com/cilium/cilium@v1.16.2/operator/api/health.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package api
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/cilium/hive/cell"
    10  	"github.com/go-openapi/runtime/middleware"
    11  	"github.com/sirupsen/logrus"
    12  	"k8s.io/client-go/discovery"
    13  
    14  	"github.com/cilium/cilium/api/v1/operator/server/restapi/operator"
    15  	k8sClient "github.com/cilium/cilium/pkg/k8s/client"
    16  	"github.com/cilium/cilium/pkg/kvstore"
    17  )
    18  
    19  type kvstoreEnabledFunc func() bool
    20  type isOperatorLeadingFunc func() bool
    21  
    22  func HealthHandlerCell(
    23  	kvstoreEnabled kvstoreEnabledFunc,
    24  	isOperatorLeading isOperatorLeadingFunc,
    25  ) cell.Cell {
    26  	return cell.Module(
    27  		"health-handler",
    28  		"Operator health HTTP handler",
    29  
    30  		cell.Provide(func(
    31  			clientset k8sClient.Clientset,
    32  			logger logrus.FieldLogger,
    33  		) operator.GetHealthzHandler {
    34  			if !clientset.IsEnabled() {
    35  				return &healthHandler{
    36  					enabled: false,
    37  				}
    38  			}
    39  
    40  			return &healthHandler{
    41  				enabled:           true,
    42  				isOperatorLeading: isOperatorLeading,
    43  				kvstoreEnabled:    kvstoreEnabled,
    44  				discovery:         clientset.Discovery(),
    45  				log:               logger,
    46  			}
    47  		}),
    48  	)
    49  }
    50  
    51  type healthHandler struct {
    52  	enabled           bool
    53  	isOperatorLeading isOperatorLeadingFunc
    54  	kvstoreEnabled    kvstoreEnabledFunc
    55  	discovery         discovery.DiscoveryInterface
    56  	log               logrus.FieldLogger
    57  }
    58  
    59  func (h *healthHandler) Handle(params operator.GetHealthzParams) middleware.Responder {
    60  	if !h.enabled {
    61  		return operator.NewGetHealthzNotImplemented()
    62  	}
    63  
    64  	if err := h.checkStatus(); err != nil {
    65  		h.log.WithError(err).Warn("Health check status")
    66  		return operator.NewGetHealthzInternalServerError().WithPayload(err.Error())
    67  	}
    68  
    69  	return operator.NewGetHealthzOK().WithPayload("ok")
    70  }
    71  
    72  // checkStatus verifies the connection status to the kvstore and the
    73  // k8s apiserver and returns an error if any of them is unhealthy
    74  func (h *healthHandler) checkStatus() error {
    75  	// We check if we are the leader here because only the leader has
    76  	// access to the kvstore client. Otherwise, the kvstore client check
    77  	// will block. It is safe for a non-leader to skip this check, as the
    78  	// it is the leader's responsibility to report the status of the
    79  	// kvstore client.
    80  	if h.isOperatorLeading() && h.kvstoreEnabled() {
    81  		client := kvstore.Client()
    82  		if client == nil {
    83  			return errors.New("kvstore client not configured")
    84  		}
    85  		if _, err := client.Status(); err != nil {
    86  			return err
    87  		}
    88  	}
    89  	_, err := h.discovery.ServerVersion()
    90  	return err
    91  }