2021-04-06 05:12:06 +02:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/go-pkgz/lgr"
|
2021-04-09 05:07:15 +02:00
|
|
|
"github.com/go-pkgz/rest"
|
|
|
|
|
2021-04-06 05:12:06 +02:00
|
|
|
"github.com/umputun/reproxy/app/discovery"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (h *Http) healthMiddleware(next http.Handler) http.Handler {
|
|
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method == "GET" && strings.HasSuffix(strings.ToLower(r.URL.Path), "/health") {
|
|
|
|
h.healthHandler(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
return http.HandlerFunc(fn)
|
|
|
|
}
|
|
|
|
|
2021-04-09 22:05:22 +02:00
|
|
|
func (h *Http) healthHandler(w http.ResponseWriter, _ *http.Request) {
|
2021-04-06 05:12:06 +02:00
|
|
|
|
|
|
|
// runs pings in parallel
|
2021-04-09 22:05:22 +02:00
|
|
|
check := func(mappers []discovery.URLMapper) (ok bool, valid int, total int, errs []string) {
|
2021-04-06 05:12:06 +02:00
|
|
|
outCh := make(chan error, 8)
|
|
|
|
var pinged int32
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for _, m := range mappers {
|
|
|
|
if m.PingURL == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
2021-04-09 22:05:22 +02:00
|
|
|
go func(m discovery.URLMapper) {
|
2021-04-06 05:12:06 +02:00
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
atomic.AddInt32(&pinged, 1)
|
|
|
|
client := http.Client{Timeout: 100 * time.Millisecond}
|
|
|
|
resp, err := client.Get(m.PingURL)
|
|
|
|
if err != nil {
|
2021-04-09 05:07:15 +02:00
|
|
|
errMsg := strings.Replace(err.Error(), "\"", "", -1)
|
|
|
|
log.Printf("[WARN] failed to ping for health %s, %s", m.PingURL, errMsg)
|
|
|
|
outCh <- fmt.Errorf("%s, %v", m.PingURL, errMsg)
|
2021-04-06 05:12:06 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
log.Printf("[WARN] failed ping status for health %s (%s)", m.PingURL, resp.Status)
|
|
|
|
outCh <- fmt.Errorf("%s, %s", m.PingURL, resp.Status)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(outCh)
|
|
|
|
}()
|
|
|
|
|
|
|
|
for e := range outCh {
|
|
|
|
errs = append(errs, e.Error())
|
|
|
|
}
|
|
|
|
return len(errs) == 0, int(atomic.LoadInt32(&pinged)) - len(errs), len(mappers), errs
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
|
|
ok, valid, total, errs := check(h.Mappers())
|
|
|
|
if !ok {
|
|
|
|
w.WriteHeader(http.StatusExpectationFailed)
|
2021-04-09 05:07:15 +02:00
|
|
|
|
|
|
|
errResp := struct {
|
|
|
|
Status string `json:"status,omitempty"`
|
|
|
|
Passed int `json:"passed,omitempty"`
|
|
|
|
Failed int `json:"failed,omitempty"`
|
|
|
|
Errors []string `json:"errors,omitempty"`
|
|
|
|
}{Status: "failed", Passed: valid, Failed: total - valid, Errors: errs}
|
|
|
|
|
|
|
|
rest.RenderJSON(w, errResp)
|
2021-04-06 05:12:06 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
_, err := fmt.Fprintf(w, `{"status": "ok", "services": %d}`, valid)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[WARN] failed to send halth, %v", err)
|
|
|
|
}
|
|
|
|
}
|