github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/web/checker.go (about) 1 package web 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "time" 8 9 "github.com/go-chi/chi" 10 "github.com/hellofresh/health-go/v3" 11 "github.com/hellofresh/janus/pkg/api" 12 log "github.com/sirupsen/logrus" 13 ) 14 15 // NewOverviewHandler creates instance of all status checks handler 16 func NewOverviewHandler(cfg *api.Configuration) func(w http.ResponseWriter, r *http.Request) { 17 return func(w http.ResponseWriter, r *http.Request) { 18 defs := findValidAPIHealthChecks(cfg.Definitions) 19 20 log.WithField("len", len(defs)).Debug("Loading health check endpoints") 21 health.Reset() 22 23 for _, def := range defs { 24 log.WithField("name", def.Name).Debug("Registering health check") 25 health.Register(health.Config{ 26 Name: def.Name, 27 Timeout: time.Second * time.Duration(def.HealthCheck.Timeout), 28 SkipOnErr: true, 29 Check: check(def), 30 }) 31 } 32 33 health.HandlerFunc(w, r) 34 } 35 } 36 37 // NewStatusHandler creates instance of single proxy status check handler 38 func NewStatusHandler(cfgs *api.Configuration) func(w http.ResponseWriter, r *http.Request) { 39 return func(w http.ResponseWriter, r *http.Request) { 40 defs := findValidAPIHealthChecks(cfgs.Definitions) 41 42 name := chi.URLParam(r, "name") 43 for _, def := range defs { 44 if name == def.Name { 45 resp, err := doStatusRequest(def, false) 46 if err != nil { 47 log.WithField("name", name).WithError(err).Error("Error requesting service health status") 48 w.WriteHeader(http.StatusInternalServerError) 49 w.Write([]byte(err.Error())) 50 return 51 } 52 53 body, err := ioutil.ReadAll(resp.Body) 54 if closeErr := resp.Body.Close(); closeErr != nil { 55 log.WithField("name", name).WithError(closeErr).Error("Error closing health status body") 56 } 57 58 if err != nil { 59 log.WithField("name", name).WithError(err).Error("Error reading health status body") 60 w.WriteHeader(http.StatusInternalServerError) 61 w.Write([]byte(err.Error())) 62 return 63 } 64 65 w.WriteHeader(resp.StatusCode) 66 w.Write(body) 67 return 68 } 69 } 70 71 w.WriteHeader(http.StatusNotFound) 72 w.Write([]byte("Definition name is not found")) 73 } 74 } 75 76 func doStatusRequest(def *api.Definition, closeBody bool) (*http.Response, error) { 77 req, err := http.NewRequest(http.MethodGet, def.HealthCheck.URL, nil) 78 if err != nil { 79 log.WithError(err).Error("Creating the request for the health check failed") 80 return nil, err 81 } 82 83 // Inform to close the connection after the transaction is complete 84 req.Header.Set("Connection", "close") 85 86 resp, err := http.DefaultClient.Do(req) 87 if err != nil { 88 log.WithError(err).Error("Making the request for the health check failed") 89 return resp, err 90 } 91 92 if closeBody { 93 defer resp.Body.Close() 94 } 95 96 return resp, err 97 } 98 99 func check(def *api.Definition) func() error { 100 return func() error { 101 resp, err := doStatusRequest(def, true) 102 if err != nil { 103 return fmt.Errorf("%s health check endpoint %s is unreachable", def.Name, def.HealthCheck.URL) 104 } 105 106 if resp.StatusCode >= http.StatusInternalServerError { 107 return fmt.Errorf("%s is not available at the moment", def.Name) 108 } 109 110 if resp.StatusCode >= http.StatusBadRequest { 111 return fmt.Errorf("%s is partially available at the moment", def.Name) 112 } 113 114 return nil 115 } 116 } 117 118 func findValidAPIHealthChecks(defs []*api.Definition) []*api.Definition { 119 var validDefs []*api.Definition 120 121 for _, def := range defs { 122 if def.Active && def.HealthCheck.URL != "" { 123 validDefs = append(validDefs, def) 124 } 125 } 126 127 return validDefs 128 }