monitor.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package tsconsensus
  4. import (
  5. "context"
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "log"
  10. "net/http"
  11. "slices"
  12. "tailscale.com/ipn"
  13. "tailscale.com/ipn/ipnstate"
  14. "tailscale.com/tsnet"
  15. "tailscale.com/util/dnsname"
  16. )
  17. type status struct {
  18. Status *ipnstate.Status
  19. RaftState string
  20. }
  21. type monitor struct {
  22. ts *tsnet.Server
  23. con *Consensus
  24. sg statusGetter
  25. }
  26. func (m *monitor) getStatus(ctx context.Context) (status, error) {
  27. tStatus, err := m.sg.getStatus(ctx)
  28. if err != nil {
  29. return status{}, err
  30. }
  31. return status{Status: tStatus, RaftState: m.con.raft.State().String()}, nil
  32. }
  33. func serveMonitor(c *Consensus, ts *tsnet.Server, listenAddr string) (*http.Server, error) {
  34. ln, err := ts.Listen("tcp", listenAddr)
  35. if err != nil {
  36. return nil, err
  37. }
  38. m := &monitor{con: c, ts: ts, sg: &tailscaleStatusGetter{
  39. ts: ts,
  40. }}
  41. mux := http.NewServeMux()
  42. mux.HandleFunc("GET /full", m.handleFullStatus)
  43. mux.HandleFunc("GET /{$}", m.handleSummaryStatus)
  44. mux.HandleFunc("GET /netmap", m.handleNetmap)
  45. mux.HandleFunc("POST /dial", m.handleDial)
  46. srv := &http.Server{Handler: mux}
  47. go func() {
  48. err := srv.Serve(ln)
  49. log.Printf("MonitorHTTP stopped serving with error: %v", err)
  50. }()
  51. return srv, nil
  52. }
  53. func (m *monitor) handleFullStatus(w http.ResponseWriter, r *http.Request) {
  54. s, err := m.getStatus(r.Context())
  55. if err != nil {
  56. log.Printf("monitor: error getStatus: %v", err)
  57. http.Error(w, "", http.StatusInternalServerError)
  58. return
  59. }
  60. if err := json.NewEncoder(w).Encode(s); err != nil {
  61. log.Printf("monitor: error encoding full status: %v", err)
  62. return
  63. }
  64. }
  65. func (m *monitor) handleSummaryStatus(w http.ResponseWriter, r *http.Request) {
  66. s, err := m.getStatus(r.Context())
  67. if err != nil {
  68. log.Printf("monitor: error getStatus: %v", err)
  69. http.Error(w, "", http.StatusInternalServerError)
  70. return
  71. }
  72. lines := []string{}
  73. for _, p := range s.Status.Peer {
  74. if p.Online {
  75. name := dnsname.FirstLabel(p.DNSName)
  76. lines = append(lines, fmt.Sprintf("%s\t\t%d\t%d\t%t", name, p.RxBytes, p.TxBytes, p.Active))
  77. }
  78. }
  79. _, err = w.Write([]byte(fmt.Sprintf("RaftState: %s\n", s.RaftState)))
  80. if err != nil {
  81. log.Printf("monitor: error writing status: %v", err)
  82. return
  83. }
  84. slices.Sort(lines)
  85. for _, ln := range lines {
  86. _, err = w.Write([]byte(fmt.Sprintf("%s\n", ln)))
  87. if err != nil {
  88. log.Printf("monitor: error writing status: %v", err)
  89. return
  90. }
  91. }
  92. }
  93. func (m *monitor) handleNetmap(w http.ResponseWriter, r *http.Request) {
  94. lc, err := m.ts.LocalClient()
  95. if err != nil {
  96. log.Printf("monitor: error LocalClient: %v", err)
  97. http.Error(w, "", http.StatusInternalServerError)
  98. return
  99. }
  100. watcher, err := lc.WatchIPNBus(r.Context(), ipn.NotifyInitialNetMap)
  101. if err != nil {
  102. log.Printf("monitor: error WatchIPNBus: %v", err)
  103. http.Error(w, "", http.StatusInternalServerError)
  104. return
  105. }
  106. defer watcher.Close()
  107. n, err := watcher.Next()
  108. if err != nil {
  109. log.Printf("monitor: error watcher.Next: %v", err)
  110. http.Error(w, "", http.StatusInternalServerError)
  111. return
  112. }
  113. encoder := json.NewEncoder(w)
  114. encoder.SetIndent("", "\t")
  115. if err := encoder.Encode(n); err != nil {
  116. log.Printf("monitor: error encoding netmap: %v", err)
  117. return
  118. }
  119. }
  120. func (m *monitor) handleDial(w http.ResponseWriter, r *http.Request) {
  121. var dialParams struct {
  122. Addr string
  123. }
  124. defer r.Body.Close()
  125. bs, err := io.ReadAll(http.MaxBytesReader(w, r.Body, maxBodyBytes))
  126. if err != nil {
  127. log.Printf("monitor: error reading body: %v", err)
  128. http.Error(w, "", http.StatusInternalServerError)
  129. return
  130. }
  131. err = json.Unmarshal(bs, &dialParams)
  132. if err != nil {
  133. log.Printf("monitor: error unmarshalling json: %v", err)
  134. http.Error(w, "", http.StatusBadRequest)
  135. return
  136. }
  137. c, err := m.ts.Dial(r.Context(), "tcp", dialParams.Addr)
  138. if err != nil {
  139. log.Printf("monitor: error dialing: %v", err)
  140. http.Error(w, "", http.StatusInternalServerError)
  141. return
  142. }
  143. c.Close()
  144. w.Write([]byte("ok\n"))
  145. }