|
5 | 5 | "encoding/json"
|
6 | 6 | "errors"
|
7 | 7 | "fmt"
|
| 8 | + "html/template" |
8 | 9 | "io"
|
9 | 10 | "net"
|
10 | 11 | "net/http"
|
@@ -646,136 +647,204 @@ func (c *coordinator) ServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
|
646 | 647 | }
|
647 | 648 |
|
648 | 649 | func (c *core) serveHTTPDebug(w http.ResponseWriter, r *http.Request) {
|
649 |
| - w.Header().Set("Content-Type", "text/html; charset=utf-8") |
650 |
| - |
651 | 650 | c.mutex.RLock()
|
652 | 651 | defer c.mutex.RUnlock()
|
653 | 652 |
|
654 |
| - _, _ = fmt.Fprintln(w, "<h1>in-memory wireguard coordinator debug</h1>") |
655 |
| - |
656 |
| - CoordinatorHTTPDebug(c.agentSockets, c.agentToConnectionSockets, c.agentNameCache)(w, r) |
| 653 | + CoordinatorHTTPDebug(false, c.agentSockets, c.agentToConnectionSockets, c.nodes, c.agentNameCache)(w, r) |
657 | 654 | }
|
658 | 655 |
|
659 | 656 | func CoordinatorHTTPDebug(
|
| 657 | + ha bool, |
660 | 658 | agentSocketsMap map[uuid.UUID]Queue,
|
661 | 659 | agentToConnectionSocketsMap map[uuid.UUID]map[uuid.UUID]Queue,
|
| 660 | + nodesMap map[uuid.UUID]*Node, |
662 | 661 | agentNameCache *lru.Cache[uuid.UUID, string],
|
663 | 662 | ) func(w http.ResponseWriter, _ *http.Request) {
|
664 | 663 | return func(w http.ResponseWriter, _ *http.Request) {
|
665 |
| - now := time.Now() |
666 |
| - |
667 |
| - type idConn struct { |
668 |
| - id uuid.UUID |
669 |
| - conn Queue |
| 664 | + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 665 | + |
| 666 | + tmpl, err := template.New("coordinator_debug").Funcs(template.FuncMap{ |
| 667 | + "marshal": func(v interface{}) template.JS { |
| 668 | + a, err := json.MarshalIndent(v, "", " ") |
| 669 | + if err != nil { |
| 670 | + //nolint:gosec |
| 671 | + return template.JS(fmt.Sprintf(`{"err": %q}`, err)) |
| 672 | + } |
| 673 | + //nolint:gosec |
| 674 | + return template.JS(a) |
| 675 | + }, |
| 676 | + }).Parse(coordinatorDebugTmpl) |
| 677 | + if err != nil { |
| 678 | + w.WriteHeader(http.StatusInternalServerError) |
| 679 | + _, _ = w.Write([]byte(err.Error())) |
| 680 | + return |
670 | 681 | }
|
671 | 682 |
|
672 |
| - { |
673 |
| - _, _ = fmt.Fprintf(w, "<h2 id=agents><a href=#agents>#</a> agents: total %d</h2>\n", len(agentSocketsMap)) |
674 |
| - _, _ = fmt.Fprintln(w, "<ul>") |
675 |
| - agentSockets := make([]idConn, 0, len(agentSocketsMap)) |
676 |
| - |
677 |
| - for id, conn := range agentSocketsMap { |
678 |
| - agentSockets = append(agentSockets, idConn{id, conn}) |
| 683 | + now := time.Now() |
| 684 | + data := htmlDebug{HA: ha} |
| 685 | + for id, conn := range agentSocketsMap { |
| 686 | + start, lastWrite := conn.Stats() |
| 687 | + agent := &htmlAgent{ |
| 688 | + Name: conn.Name(), |
| 689 | + ID: id, |
| 690 | + CreatedAge: now.Sub(time.Unix(start, 0)).Round(time.Second), |
| 691 | + LastWriteAge: now.Sub(time.Unix(lastWrite, 0)).Round(time.Second), |
| 692 | + Overwrites: int(conn.Overwrites()), |
679 | 693 | }
|
680 | 694 |
|
681 |
| - slices.SortFunc(agentSockets, func(a, b idConn) bool { |
682 |
| - return a.conn.Name() < b.conn.Name() |
683 |
| - }) |
684 |
| - |
685 |
| - for _, agent := range agentSockets { |
686 |
| - start, lastWrite := agent.conn.Stats() |
687 |
| - _, _ = fmt.Fprintf(w, "<li style=\"margin-top:4px\"><b>%s</b> (<code>%s</code>): created %v ago, write %v ago, overwrites %d </li>\n", |
688 |
| - agent.conn.Name(), |
689 |
| - agent.id.String(), |
690 |
| - now.Sub(time.Unix(start, 0)).Round(time.Second), |
691 |
| - now.Sub(time.Unix(lastWrite, 0)).Round(time.Second), |
692 |
| - agent.conn.Overwrites(), |
693 |
| - ) |
694 |
| - |
695 |
| - if conns := agentToConnectionSocketsMap[agent.id]; len(conns) > 0 { |
696 |
| - _, _ = fmt.Fprintf(w, "<h3 style=\"margin:0px;font-size:16px;font-weight:400\">connections: total %d</h3>\n", len(conns)) |
697 |
| - |
698 |
| - connSockets := make([]idConn, 0, len(conns)) |
699 |
| - for id, conn := range conns { |
700 |
| - connSockets = append(connSockets, idConn{id, conn}) |
701 |
| - } |
702 |
| - slices.SortFunc(connSockets, func(a, b idConn) bool { |
703 |
| - return a.id.String() < b.id.String() |
704 |
| - }) |
705 |
| - |
706 |
| - _, _ = fmt.Fprintln(w, "<ul>") |
707 |
| - for _, connSocket := range connSockets { |
708 |
| - start, lastWrite := connSocket.conn.Stats() |
709 |
| - _, _ = fmt.Fprintf(w, "<li><b>%s</b> (<code>%s</code>): created %v ago, write %v ago </li>\n", |
710 |
| - connSocket.conn.Name(), |
711 |
| - connSocket.id.String(), |
712 |
| - now.Sub(time.Unix(start, 0)).Round(time.Second), |
713 |
| - now.Sub(time.Unix(lastWrite, 0)).Round(time.Second), |
714 |
| - ) |
715 |
| - } |
716 |
| - _, _ = fmt.Fprintln(w, "</ul>") |
717 |
| - } |
| 695 | + for id, conn := range agentToConnectionSocketsMap[id] { |
| 696 | + start, lastWrite := conn.Stats() |
| 697 | + agent.Connections = append(agent.Connections, &htmlClient{ |
| 698 | + Name: conn.Name(), |
| 699 | + ID: id, |
| 700 | + CreatedAge: now.Sub(time.Unix(start, 0)).Round(time.Second), |
| 701 | + LastWriteAge: now.Sub(time.Unix(lastWrite, 0)).Round(time.Second), |
| 702 | + }) |
718 | 703 | }
|
| 704 | + slices.SortFunc(agent.Connections, func(a, b *htmlClient) bool { |
| 705 | + return a.Name < b.Name |
| 706 | + }) |
719 | 707 |
|
720 |
| - _, _ = fmt.Fprintln(w, "</ul>") |
| 708 | + data.Agents = append(data.Agents, agent) |
721 | 709 | }
|
| 710 | + slices.SortFunc(data.Agents, func(a, b *htmlAgent) bool { |
| 711 | + return a.Name < b.Name |
| 712 | + }) |
722 | 713 |
|
723 |
| - { |
724 |
| - type agentConns struct { |
725 |
| - id uuid.UUID |
726 |
| - conns []idConn |
| 714 | + for agentID, conns := range agentToConnectionSocketsMap { |
| 715 | + if len(conns) == 0 { |
| 716 | + continue |
727 | 717 | }
|
728 | 718 |
|
729 |
| - missingAgents := []agentConns{} |
730 |
| - for agentID, conns := range agentToConnectionSocketsMap { |
731 |
| - if len(conns) == 0 { |
732 |
| - continue |
| 719 | + if _, ok := agentSocketsMap[agentID]; !ok { |
| 720 | + agentName, ok := agentNameCache.Get(agentID) |
| 721 | + if !ok { |
| 722 | + agentName = "unknown" |
733 | 723 | }
|
734 |
| - |
735 |
| - if _, ok := agentSocketsMap[agentID]; !ok { |
736 |
| - connsSlice := make([]idConn, 0, len(conns)) |
737 |
| - for id, conn := range conns { |
738 |
| - connsSlice = append(connsSlice, idConn{id, conn}) |
739 |
| - } |
740 |
| - slices.SortFunc(connsSlice, func(a, b idConn) bool { |
741 |
| - return a.id.String() < b.id.String() |
| 724 | + agent := &htmlAgent{ |
| 725 | + Name: agentName, |
| 726 | + ID: agentID, |
| 727 | + } |
| 728 | + for id, conn := range conns { |
| 729 | + start, lastWrite := conn.Stats() |
| 730 | + agent.Connections = append(agent.Connections, &htmlClient{ |
| 731 | + Name: conn.Name(), |
| 732 | + ID: id, |
| 733 | + CreatedAge: now.Sub(time.Unix(start, 0)).Round(time.Second), |
| 734 | + LastWriteAge: now.Sub(time.Unix(lastWrite, 0)).Round(time.Second), |
742 | 735 | })
|
743 |
| - |
744 |
| - missingAgents = append(missingAgents, agentConns{agentID, connsSlice}) |
745 | 736 | }
|
| 737 | + slices.SortFunc(agent.Connections, func(a, b *htmlClient) bool { |
| 738 | + return a.Name < b.Name |
| 739 | + }) |
| 740 | + |
| 741 | + data.MissingAgents = append(data.MissingAgents, agent) |
746 | 742 | }
|
747 |
| - slices.SortFunc(missingAgents, func(a, b agentConns) bool { |
748 |
| - return a.id.String() < b.id.String() |
| 743 | + } |
| 744 | + slices.SortFunc(data.MissingAgents, func(a, b *htmlAgent) bool { |
| 745 | + return a.Name < b.Name |
| 746 | + }) |
| 747 | + |
| 748 | + for id, node := range nodesMap { |
| 749 | + name, _ := agentNameCache.Get(id) |
| 750 | + data.Nodes = append(data.Nodes, &htmlNode{ |
| 751 | + ID: id, |
| 752 | + Name: name, |
| 753 | + Node: node, |
749 | 754 | })
|
| 755 | + } |
| 756 | + slices.SortFunc(data.Nodes, func(a, b *htmlNode) bool { |
| 757 | + return a.Name+a.ID.String() < b.Name+b.ID.String() |
| 758 | + }) |
750 | 759 |
|
751 |
| - _, _ = fmt.Fprintf(w, "<h2 id=missing-agents><a href=#missing-agents>#</a> missing agents: total %d</h2>\n", len(missingAgents)) |
752 |
| - _, _ = fmt.Fprintln(w, "<ul>") |
753 |
| - |
754 |
| - for _, agentConns := range missingAgents { |
755 |
| - agentName, ok := agentNameCache.Get(agentConns.id) |
756 |
| - if !ok { |
757 |
| - agentName = "unknown" |
758 |
| - } |
759 |
| - |
760 |
| - _, _ = fmt.Fprintf(w, "<li style=\"margin-top:4px\"><b>%s</b> (<code>%s</code>): created ? ago, write ? ago, overwrites ? </li>\n", |
761 |
| - agentName, |
762 |
| - agentConns.id.String(), |
763 |
| - ) |
764 |
| - |
765 |
| - _, _ = fmt.Fprintf(w, "<h3 style=\"margin:0px;font-size:16px;font-weight:400\">connections: total %d</h3>\n", len(agentConns.conns)) |
766 |
| - _, _ = fmt.Fprintln(w, "<ul>") |
767 |
| - for _, agentConn := range agentConns.conns { |
768 |
| - start, lastWrite := agentConn.conn.Stats() |
769 |
| - _, _ = fmt.Fprintf(w, "<li><b>%s</b> (<code>%s</code>): created %v ago, write %v ago </li>\n", |
770 |
| - agentConn.conn.Name(), |
771 |
| - agentConn.id.String(), |
772 |
| - now.Sub(time.Unix(start, 0)).Round(time.Second), |
773 |
| - now.Sub(time.Unix(lastWrite, 0)).Round(time.Second), |
774 |
| - ) |
775 |
| - } |
776 |
| - _, _ = fmt.Fprintln(w, "</ul>") |
777 |
| - } |
778 |
| - _, _ = fmt.Fprintln(w, "</ul>") |
| 760 | + err = tmpl.Execute(w, data) |
| 761 | + if err != nil { |
| 762 | + w.WriteHeader(http.StatusInternalServerError) |
| 763 | + _, _ = w.Write([]byte(err.Error())) |
| 764 | + return |
779 | 765 | }
|
780 | 766 | }
|
781 | 767 | }
|
| 768 | + |
| 769 | +type htmlDebug struct { |
| 770 | + HA bool |
| 771 | + Agents []*htmlAgent |
| 772 | + MissingAgents []*htmlAgent |
| 773 | + Nodes []*htmlNode |
| 774 | +} |
| 775 | + |
| 776 | +type htmlAgent struct { |
| 777 | + Name string |
| 778 | + ID uuid.UUID |
| 779 | + CreatedAge time.Duration |
| 780 | + LastWriteAge time.Duration |
| 781 | + Overwrites int |
| 782 | + Connections []*htmlClient |
| 783 | +} |
| 784 | + |
| 785 | +type htmlClient struct { |
| 786 | + Name string |
| 787 | + ID uuid.UUID |
| 788 | + CreatedAge time.Duration |
| 789 | + LastWriteAge time.Duration |
| 790 | +} |
| 791 | + |
| 792 | +type htmlNode struct { |
| 793 | + ID uuid.UUID |
| 794 | + Name string |
| 795 | + Node *Node |
| 796 | +} |
| 797 | + |
| 798 | +var coordinatorDebugTmpl = ` |
| 799 | +<!DOCTYPE html> |
| 800 | +<html> |
| 801 | + <head> |
| 802 | + <meta charset="UTF-8"> |
| 803 | + </head> |
| 804 | + <body> |
| 805 | + {{- if .HA }} |
| 806 | + <h1>high-availability wireguard coordinator debug</h1> |
| 807 | + <h4 style="margin-top:-25px">warning: this only provides info from the node that served the request, if there are multiple replicas this data may be incomplete</h4> |
| 808 | + {{- else }} |
| 809 | + <h1>in-memory wireguard coordinator debug</h1> |
| 810 | + {{- end }} |
| 811 | +
|
| 812 | + <h2 id=agents> <a href=#agents>#</a> agents: total {{ len .Agents }} </h2> |
| 813 | + <ul> |
| 814 | + {{- range .Agents }} |
| 815 | + <li style="margin-top:4px"> |
| 816 | + <b>{{ .Name }}</b> (<code>{{ .ID }}</code>): created {{ .CreatedAge }} ago, write {{ .LastWriteAge }} ago, overwrites {{ .Overwrites }} |
| 817 | + <h3 style="margin:0px;font-size:16px;font-weight:400"> connections: total {{ len .Connections}} </h3> |
| 818 | + <ul> |
| 819 | + {{- range .Connections }} |
| 820 | + <li><b>{{ .Name }}</b> (<code>{{ .ID }}</code>): created {{ .CreatedAge }} ago, write {{ .LastWriteAge }} ago </li> |
| 821 | + {{- end }} |
| 822 | + </ul> |
| 823 | + </li> |
| 824 | + {{- end }} |
| 825 | + </ul> |
| 826 | +
|
| 827 | + <h2 id=missing-agents><a href=#missing-agents>#</a> missing agents: total {{ len .MissingAgents }}</h2> |
| 828 | + <ul> |
| 829 | + {{- range .MissingAgents}} |
| 830 | + <li style="margin-top:4px"><b>{{ .Name }}</b> (<code>{{ .ID }}</code>): created ? ago, write ? ago, overwrites ? </li> |
| 831 | + <h3 style="margin:0px;font-size:16px;font-weight:400"> connections: total {{ len .Connections }} </h3> |
| 832 | + <ul> |
| 833 | + {{- range .Connections }} |
| 834 | + <li><b>{{ .Name }}</b> (<code>{{ .ID }}</code>): created {{ .CreatedAge }} ago, write {{ .LastWriteAge }} ago </li> |
| 835 | + {{- end }} |
| 836 | + </ul> |
| 837 | + {{- end }} |
| 838 | + </ul> |
| 839 | +
|
| 840 | + <h2 id=nodes><a href=#nodes>#</a> nodes: total {{ len .Nodes }}</h2> |
| 841 | + <ul> |
| 842 | + {{- range .Nodes }} |
| 843 | + <li style="margin-top:4px"><b>{{ .Name }}</b> (<code>{{ .ID }}</code>): |
| 844 | + <span style="white-space: pre;"><code>{{ marshal .Node }}</code></span> |
| 845 | + </li> |
| 846 | + {{- end }} |
| 847 | + </ul> |
| 848 | + </body> |
| 849 | +</html> |
| 850 | +` |
0 commit comments