mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-08 12:01:20 +00:00
47df98a499
log.Warn/Error uses "err" key to log errors in most places. This renames "error" to "err" in some places to adhere to this convention.
133 lines
2.6 KiB
Go
133 lines
2.6 KiB
Go
package health
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/ledgerwatch/erigon/rpc"
|
|
"github.com/ledgerwatch/log/v3"
|
|
)
|
|
|
|
type requestBody struct {
|
|
MinPeerCount *uint `json:"min_peer_count"`
|
|
BlockNumber *rpc.BlockNumber `json:"known_block"`
|
|
}
|
|
|
|
const (
|
|
urlPath = "/health"
|
|
)
|
|
|
|
var (
|
|
errCheckDisabled = errors.New("error check disabled")
|
|
)
|
|
|
|
func ProcessHealthcheckIfNeeded(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
rpcAPI []rpc.API,
|
|
) bool {
|
|
if !strings.EqualFold(r.URL.Path, urlPath) {
|
|
return false
|
|
}
|
|
|
|
netAPI, ethAPI := parseAPI(rpcAPI)
|
|
|
|
var errMinPeerCount = errCheckDisabled
|
|
var errCheckBlock = errCheckDisabled
|
|
|
|
body, errParse := parseHealthCheckBody(r.Body)
|
|
defer r.Body.Close()
|
|
|
|
if errParse != nil {
|
|
log.Root().Warn("unable to process healthcheck request", "err", errParse)
|
|
} else {
|
|
// 1. net_peerCount
|
|
if body.MinPeerCount != nil {
|
|
errMinPeerCount = checkMinPeers(*body.MinPeerCount, netAPI)
|
|
}
|
|
// 2. custom query (shouldn't fail)
|
|
if body.BlockNumber != nil {
|
|
errCheckBlock = checkBlockNumber(*body.BlockNumber, ethAPI)
|
|
}
|
|
// TODO add time from the last sync cycle
|
|
}
|
|
|
|
err := reportHealth(errParse, errMinPeerCount, errCheckBlock, w)
|
|
if err != nil {
|
|
log.Root().Warn("unable to process healthcheck request", "err", err)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func parseHealthCheckBody(reader io.Reader) (requestBody, error) {
|
|
var body requestBody
|
|
|
|
bodyBytes, err := ioutil.ReadAll(reader)
|
|
if err != nil {
|
|
return body, err
|
|
}
|
|
|
|
err = json.Unmarshal(bodyBytes, &body)
|
|
if err != nil {
|
|
return body, err
|
|
}
|
|
|
|
return body, nil
|
|
}
|
|
|
|
func reportHealth(errParse, errMinPeerCount, errCheckBlock error, w http.ResponseWriter) error {
|
|
statusCode := http.StatusOK
|
|
errors := make(map[string]string)
|
|
|
|
if shouldChangeStatusCode(errParse) {
|
|
statusCode = http.StatusInternalServerError
|
|
}
|
|
errors["healthcheck_query"] = errorStringOrOK(errParse)
|
|
|
|
if shouldChangeStatusCode(errMinPeerCount) {
|
|
statusCode = http.StatusInternalServerError
|
|
}
|
|
errors["min_peer_count"] = errorStringOrOK(errMinPeerCount)
|
|
|
|
if shouldChangeStatusCode(errCheckBlock) {
|
|
statusCode = http.StatusInternalServerError
|
|
}
|
|
errors["check_block"] = errorStringOrOK(errCheckBlock)
|
|
|
|
w.WriteHeader(statusCode)
|
|
|
|
bodyJson, err := json.Marshal(errors)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = w.Write(bodyJson)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func shouldChangeStatusCode(err error) bool {
|
|
return err != nil && !errors.Is(err, errCheckDisabled)
|
|
}
|
|
|
|
func errorStringOrOK(err error) string {
|
|
if err == nil {
|
|
return "HEALTHY"
|
|
}
|
|
|
|
if errors.Is(err, errCheckDisabled) {
|
|
return "DISABLED"
|
|
}
|
|
|
|
return fmt.Sprintf("ERROR: %v", err)
|
|
}
|