health.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. // package readiness implements a minimal health-checking mechanism for use as k8s readiness probes. It will always
  2. // return a "ready" state after the conditions have been met for the first time - it's not meant for monitoring.
  3. //
  4. // Uses a global singleton registry (similar to the Prometheus client's default behavior).
  5. package readiness
  6. import (
  7. "bytes"
  8. "fmt"
  9. "net/http"
  10. "sync"
  11. )
  12. var (
  13. mu = sync.Mutex{}
  14. registry = map[string]bool{}
  15. )
  16. type Component string
  17. // RegisterComponent registers the given component name such that it is required to be ready for the global check to succeed.
  18. func RegisterComponent(component Component) {
  19. mu.Lock()
  20. if _, ok := registry[string(component)]; ok {
  21. panic("component already registered")
  22. }
  23. registry[string(component)] = false
  24. mu.Unlock()
  25. }
  26. // SetReady sets the given global component state.
  27. func SetReady(component Component) {
  28. mu.Lock()
  29. if !registry[string(component)] {
  30. registry[string(component)] = true
  31. }
  32. mu.Unlock()
  33. }
  34. // Handler returns a net/http handler for the readiness check. It returns 200 OK if all components are ready,
  35. // or 412 Precondition Failed otherwise. For operator convenience, a list of components and their states
  36. // is returned as plain text (not meant for machine consumption!).
  37. func Handler(w http.ResponseWriter, r *http.Request) {
  38. ready := true
  39. resp := new(bytes.Buffer)
  40. _, err := resp.Write([]byte("[not suitable for monitoring - do not parse]\n\n"))
  41. if err != nil {
  42. panic(err)
  43. }
  44. _, err = resp.Write([]byte("[these values update AT STARTUP ONLY - see https://github.com/wormhole-foundation/wormhole/blob/main/docs/operations.md#readyz]\n\n"))
  45. if err != nil {
  46. panic(err)
  47. }
  48. mu.Lock()
  49. for k, v := range registry {
  50. _, err = fmt.Fprintf(resp, "%s\t%v\n", k, v)
  51. if err != nil {
  52. panic(err)
  53. }
  54. if !v {
  55. ready = false
  56. }
  57. }
  58. mu.Unlock()
  59. if !ready {
  60. w.WriteHeader(http.StatusPreconditionFailed)
  61. } else {
  62. w.WriteHeader(http.StatusOK)
  63. }
  64. _, _ = resp.WriteTo(w)
  65. }