mirror of https://github.com/coder/coder.git
feat: implement HTMLDebug for PGCoord with v2 API (#10914)
Implements HTMLDebug for the PGCoordinator with the new v2 API and related DB tables.
This commit is contained in:
parent
18c4a98865
commit
52901e1219
|
@ -82,6 +82,7 @@ helm/**/templates/*.yaml
|
|||
|
||||
# Testdata shouldn't be formatted.
|
||||
scripts/apitypings/testdata/**/*.ts
|
||||
enterprise/tailnet/testdata/*.golden.html
|
||||
|
||||
# Generated files shouldn't be formatted.
|
||||
site/e2e/provisionerGenerated.ts
|
||||
|
|
|
@ -8,6 +8,7 @@ helm/**/templates/*.yaml
|
|||
|
||||
# Testdata shouldn't be formatted.
|
||||
scripts/apitypings/testdata/**/*.ts
|
||||
enterprise/tailnet/testdata/*.golden.html
|
||||
|
||||
# Generated files shouldn't be formatted.
|
||||
site/e2e/provisionerGenerated.ts
|
||||
|
|
14
Makefile
14
Makefile
|
@ -595,7 +595,15 @@ coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS)
|
|||
./scripts/apidocgen/generate.sh
|
||||
pnpm run format:write:only ./docs/api ./docs/manifest.json ./coderd/apidoc/swagger.json
|
||||
|
||||
update-golden-files: cli/testdata/.gen-golden helm/coder/tests/testdata/.gen-golden helm/provisioner/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden enterprise/cli/testdata/.gen-golden coderd/.gen-golden provisioner/terraform/testdata/.gen-golden
|
||||
update-golden-files: \
|
||||
cli/testdata/.gen-golden \
|
||||
helm/coder/tests/testdata/.gen-golden \
|
||||
helm/provisioner/tests/testdata/.gen-golden \
|
||||
scripts/ci-report/testdata/.gen-golden \
|
||||
enterprise/cli/testdata/.gen-golden \
|
||||
enterprise/tailnet/testdata/.gen-golden \
|
||||
coderd/.gen-golden \
|
||||
provisioner/terraform/testdata/.gen-golden
|
||||
.PHONY: update-golden-files
|
||||
|
||||
cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard cli/*_test.go)
|
||||
|
@ -606,6 +614,10 @@ enterprise/cli/testdata/.gen-golden: $(wildcard enterprise/cli/testdata/*.golden
|
|||
go test ./enterprise/cli -run="TestEnterpriseCommandHelp" -update
|
||||
touch "$@"
|
||||
|
||||
enterprise/tailnet/testdata/.gen-golden: $(wildcard enterprise/tailnet/testdata/*.golden.html) $(GO_SRC_FILES) $(wildcard enterprise/tailnet/*_test.go)
|
||||
go test ./enterprise/tailnet -run="TestDebugTemplate" -update
|
||||
touch "$@"
|
||||
|
||||
helm/coder/tests/testdata/.gen-golden: $(wildcard helm/coder/tests/testdata/*.yaml) $(wildcard helm/coder/tests/testdata/*.golden) $(GO_SRC_FILES) $(wildcard helm/coder/tests/*_test.go)
|
||||
go test ./helm/coder/tests -run=TestUpdateGoldenFiles -update
|
||||
touch "$@"
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
package tailnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
gProto "google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/tailnet/proto"
|
||||
)
|
||||
|
||||
type HTMLDebug struct {
|
||||
Coordinators []*HTMLCoordinator
|
||||
Peers []*HTMLPeer
|
||||
Tunnels []*HTMLTunnel
|
||||
}
|
||||
|
||||
type HTMLPeer struct {
|
||||
ID uuid.UUID
|
||||
CoordinatorID uuid.UUID
|
||||
LastWriteAge time.Duration
|
||||
Node string
|
||||
Status database.TailnetStatus
|
||||
}
|
||||
|
||||
type HTMLCoordinator struct {
|
||||
ID uuid.UUID
|
||||
HeartbeatAge time.Duration
|
||||
}
|
||||
|
||||
type HTMLTunnel struct {
|
||||
CoordinatorID uuid.UUID
|
||||
SrcID uuid.UUID
|
||||
DstID uuid.UUID
|
||||
LastWriteAge time.Duration
|
||||
}
|
||||
|
||||
func (c *pgCoord) ServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
debug, err := getDebug(ctx, c.store)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
err = debugTempl.Execute(w, debug)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getDebug(ctx context.Context, store database.Store) (HTMLDebug, error) {
|
||||
out := HTMLDebug{}
|
||||
coords, err := store.GetAllTailnetCoordinators(ctx)
|
||||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
|
||||
return HTMLDebug{}, xerrors.Errorf("failed to query coordinators: %w", err)
|
||||
}
|
||||
peers, err := store.GetAllTailnetPeers(ctx)
|
||||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
|
||||
return HTMLDebug{}, xerrors.Errorf("failed to query peers: %w", err)
|
||||
}
|
||||
tunnels, err := store.GetAllTailnetTunnels(ctx)
|
||||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
|
||||
return HTMLDebug{}, xerrors.Errorf("failed to query tunnels: %w", err)
|
||||
}
|
||||
now := time.Now() // call this once so all our ages are on the same timebase
|
||||
for _, coord := range coords {
|
||||
out.Coordinators = append(out.Coordinators, coordToHTML(coord, now))
|
||||
}
|
||||
for _, peer := range peers {
|
||||
ph, err := peerToHTML(peer, now)
|
||||
if err != nil {
|
||||
return HTMLDebug{}, err
|
||||
}
|
||||
out.Peers = append(out.Peers, ph)
|
||||
}
|
||||
for _, tunnel := range tunnels {
|
||||
out.Tunnels = append(out.Tunnels, tunnelToHTML(tunnel, now))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func coordToHTML(d database.TailnetCoordinator, now time.Time) *HTMLCoordinator {
|
||||
return &HTMLCoordinator{
|
||||
ID: d.ID,
|
||||
HeartbeatAge: now.Sub(d.HeartbeatAt),
|
||||
}
|
||||
}
|
||||
|
||||
func peerToHTML(d database.TailnetPeer, now time.Time) (*HTMLPeer, error) {
|
||||
node := &proto.Node{}
|
||||
err := gProto.Unmarshal(d.Node, node)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unmarshal node: %w", err)
|
||||
}
|
||||
return &HTMLPeer{
|
||||
ID: d.ID,
|
||||
CoordinatorID: d.CoordinatorID,
|
||||
LastWriteAge: now.Sub(d.UpdatedAt),
|
||||
Status: d.Status,
|
||||
Node: node.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func tunnelToHTML(d database.TailnetTunnel, now time.Time) *HTMLTunnel {
|
||||
return &HTMLTunnel{
|
||||
CoordinatorID: d.CoordinatorID,
|
||||
SrcID: d.SrcID,
|
||||
DstID: d.DstID,
|
||||
LastWriteAge: now.Sub(d.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
var coordinatorDebugTmpl = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
th, td {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
tr {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id=coordinators><a href=#coordinators>#</a> coordinators: total {{ len .Coordinators }}</h2>
|
||||
<table>
|
||||
<tr style="margin-top:4px">
|
||||
<th>ID</th>
|
||||
<th>Heartbeat Age</th>
|
||||
</tr>
|
||||
{{- range .Coordinators}}
|
||||
<tr style="margin-top:4px">
|
||||
<td>{{ .ID }}</td>
|
||||
<td>{{ .HeartbeatAge }} ago</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
|
||||
<h2 id=peers> <a href=#peers>#</a> peers: total {{ len .Peers }} </h2>
|
||||
<table>
|
||||
<tr style="margin-top:4px">
|
||||
<th>ID</th>
|
||||
<th>CoordinatorID</th>
|
||||
<th>Status</th>
|
||||
<th>Last Write Age</th>
|
||||
<th>Node</th>
|
||||
</tr>
|
||||
{{- range .Peers }}
|
||||
<tr style="margin-top:4px">
|
||||
<td>{{ .ID }}</td>
|
||||
<td>{{ .CoordinatorID }}</td>
|
||||
<td>{{ .Status }}</td>
|
||||
<td>{{ .LastWriteAge }} ago</td>
|
||||
<td style="white-space: pre;"><code>{{ .Node }}</code></td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
|
||||
<h2 id=tunnels><a href=#tunnels>#</a> tunnels: total {{ len .Tunnels }}</h2>
|
||||
<table>
|
||||
<tr style="margin-top:4px">
|
||||
<th>SrcID</th>
|
||||
<th>DstID</th>
|
||||
<th>CoordinatorID</th>
|
||||
<th>Last Write Age</th>
|
||||
</tr>
|
||||
{{- range .Tunnels }}
|
||||
<tr style="margin-top:4px">
|
||||
<td>{{ .SrcID }}</td>
|
||||
<td>{{ .DstID }}</td>
|
||||
<td>{{ .CoordinatorID }}</td>
|
||||
<td>{{ .LastWriteAge }} ago</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
var debugTempl = template.Must(template.New("coordinator_debug").Parse(coordinatorDebugTmpl))
|
|
@ -6,7 +6,6 @@ import (
|
|||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -19,7 +18,6 @@ import (
|
|||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/xerrors"
|
||||
gProto "google.golang.org/protobuf/proto"
|
||||
|
||||
|
@ -28,7 +26,6 @@ import (
|
|||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/util/slice"
|
||||
agpl "github.com/coder/coder/v2/tailnet"
|
||||
)
|
||||
|
||||
|
@ -1296,29 +1293,6 @@ func (q *querier) setHealthy() {
|
|||
q.healthy = true
|
||||
}
|
||||
|
||||
func (q *querier) getAll(ctx context.Context) (map[uuid.UUID]database.TailnetAgent, map[uuid.UUID][]database.TailnetClient, error) {
|
||||
agents, err := q.store.GetAllTailnetAgents(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("get all tailnet agents: %w", err)
|
||||
}
|
||||
agentsMap := map[uuid.UUID]database.TailnetAgent{}
|
||||
for _, agent := range agents {
|
||||
agentsMap[agent.ID] = agent
|
||||
}
|
||||
clients, err := q.store.GetAllTailnetClients(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("get all tailnet clients: %w", err)
|
||||
}
|
||||
clientsMap := map[uuid.UUID][]database.TailnetClient{}
|
||||
for _, client := range clients {
|
||||
for _, agentID := range client.AgentIds {
|
||||
clientsMap[agentID] = append(clientsMap[agentID], client.TailnetClient)
|
||||
}
|
||||
}
|
||||
|
||||
return agentsMap, clientsMap, nil
|
||||
}
|
||||
|
||||
func parseTunnelUpdate(msg string) ([]uuid.UUID, error) {
|
||||
parts := strings.Split(msg, ",")
|
||||
if len(parts) != 2 {
|
||||
|
@ -1721,91 +1695,3 @@ func (h *heartbeats) cleanup() {
|
|||
}
|
||||
h.logger.Debug(h.ctx, "cleaned up old coordinators")
|
||||
}
|
||||
|
||||
func (c *pgCoord) ServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
debug, err := c.htmlDebug(ctx)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
agpl.CoordinatorHTTPDebug(debug)(w, r)
|
||||
}
|
||||
|
||||
func (c *pgCoord) htmlDebug(ctx context.Context) (agpl.HTMLDebug, error) {
|
||||
now := time.Now()
|
||||
data := agpl.HTMLDebug{}
|
||||
agents, clients, err := c.querier.getAll(ctx)
|
||||
if err != nil {
|
||||
return data, xerrors.Errorf("get all agents and clients: %w", err)
|
||||
}
|
||||
|
||||
for _, agent := range agents {
|
||||
htmlAgent := &agpl.HTMLAgent{
|
||||
ID: agent.ID,
|
||||
// Name: ??, TODO: get agent names
|
||||
LastWriteAge: now.Sub(agent.UpdatedAt).Round(time.Second),
|
||||
}
|
||||
for _, conn := range clients[agent.ID] {
|
||||
htmlAgent.Connections = append(htmlAgent.Connections, &agpl.HTMLClient{
|
||||
ID: conn.ID,
|
||||
Name: conn.ID.String(),
|
||||
LastWriteAge: now.Sub(conn.UpdatedAt).Round(time.Second),
|
||||
})
|
||||
data.Nodes = append(data.Nodes, &agpl.HTMLNode{
|
||||
ID: conn.ID,
|
||||
Node: conn.Node,
|
||||
})
|
||||
}
|
||||
slices.SortFunc(htmlAgent.Connections, func(a, b *agpl.HTMLClient) int {
|
||||
return slice.Ascending(a.Name, b.Name)
|
||||
})
|
||||
|
||||
data.Agents = append(data.Agents, htmlAgent)
|
||||
data.Nodes = append(data.Nodes, &agpl.HTMLNode{
|
||||
ID: agent.ID,
|
||||
// Name: ??, TODO: get agent names
|
||||
Node: agent.Node,
|
||||
})
|
||||
}
|
||||
slices.SortFunc(data.Agents, func(a, b *agpl.HTMLAgent) int {
|
||||
return slice.Ascending(a.Name, b.Name)
|
||||
})
|
||||
|
||||
for agentID, conns := range clients {
|
||||
if len(conns) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := agents[agentID]; ok {
|
||||
continue
|
||||
}
|
||||
agent := &agpl.HTMLAgent{
|
||||
Name: "unknown",
|
||||
ID: agentID,
|
||||
}
|
||||
for _, conn := range conns {
|
||||
agent.Connections = append(agent.Connections, &agpl.HTMLClient{
|
||||
Name: conn.ID.String(),
|
||||
ID: conn.ID,
|
||||
LastWriteAge: now.Sub(conn.UpdatedAt).Round(time.Second),
|
||||
})
|
||||
data.Nodes = append(data.Nodes, &agpl.HTMLNode{
|
||||
ID: conn.ID,
|
||||
Node: conn.Node,
|
||||
})
|
||||
}
|
||||
slices.SortFunc(agent.Connections, func(a, b *agpl.HTMLClient) int {
|
||||
return slice.Ascending(a.Name, b.Name)
|
||||
})
|
||||
|
||||
data.MissingAgents = append(data.MissingAgents, agent)
|
||||
}
|
||||
slices.SortFunc(data.MissingAgents, func(a, b *agpl.HTMLAgent) int {
|
||||
return slice.Ascending(a.Name, b.Name)
|
||||
})
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
package tailnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
gProto "google.golang.org/protobuf/proto"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbmock"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/tailnet/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
// UpdateGoldenFiles indicates golden files should be updated.
|
||||
// To update the golden files:
|
||||
// make update-golden-files
|
||||
var UpdateGoldenFiles = flag.Bool("update", false, "update .golden files")
|
||||
|
||||
// TestHeartbeat_Cleanup is internal so that we can overwrite the cleanup period and not wait an hour for the timed
|
||||
// cleanup.
|
||||
func TestHeartbeat_Cleanup(t *testing.T) {
|
||||
|
@ -50,3 +66,122 @@ func TestHeartbeat_Cleanup(t *testing.T) {
|
|||
}
|
||||
close(waitForCleanup)
|
||||
}
|
||||
|
||||
func TestDebugTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("newlines screw up golden files on windows")
|
||||
}
|
||||
c1 := uuid.MustParse("01000000-1111-1111-1111-111111111111")
|
||||
c2 := uuid.MustParse("02000000-1111-1111-1111-111111111111")
|
||||
p1 := uuid.MustParse("01000000-2222-2222-2222-222222222222")
|
||||
p2 := uuid.MustParse("02000000-2222-2222-2222-222222222222")
|
||||
in := HTMLDebug{
|
||||
Coordinators: []*HTMLCoordinator{
|
||||
{
|
||||
ID: c1,
|
||||
HeartbeatAge: 2 * time.Second,
|
||||
},
|
||||
{
|
||||
ID: c2,
|
||||
HeartbeatAge: time.Second,
|
||||
},
|
||||
},
|
||||
Peers: []*HTMLPeer{
|
||||
{
|
||||
ID: p1,
|
||||
CoordinatorID: c1,
|
||||
LastWriteAge: 5 * time.Second,
|
||||
Status: database.TailnetStatusOk,
|
||||
Node: `id:1 preferred_derp:999 endpoints:"192.168.0.49:4449"`,
|
||||
},
|
||||
{
|
||||
ID: p2,
|
||||
CoordinatorID: c2,
|
||||
LastWriteAge: 7 * time.Second,
|
||||
Status: database.TailnetStatusLost,
|
||||
Node: `id:2 preferred_derp:999 endpoints:"192.168.0.33:4449"`,
|
||||
},
|
||||
},
|
||||
Tunnels: []*HTMLTunnel{
|
||||
{
|
||||
CoordinatorID: c1,
|
||||
SrcID: p1,
|
||||
DstID: p2,
|
||||
LastWriteAge: 3 * time.Second,
|
||||
},
|
||||
},
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err := debugTempl.Execute(buf, in)
|
||||
require.NoError(t, err)
|
||||
actual := buf.Bytes()
|
||||
|
||||
goldenPath := filepath.Join("testdata", "debug.golden.html")
|
||||
if *UpdateGoldenFiles {
|
||||
t.Logf("update golden file %s", goldenPath)
|
||||
err := os.WriteFile(goldenPath, actual, 0o600)
|
||||
require.NoError(t, err, "update golden file")
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
require.NoError(t, err, "read golden file, run \"make update-golden-files\" and commit the changes")
|
||||
|
||||
require.Equal(
|
||||
t, string(expected), string(actual),
|
||||
"golden file mismatch: %s, run \"make update-golden-files\", verify and commit the changes",
|
||||
goldenPath,
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetDebug(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !dbtestutil.WillUsePostgres() {
|
||||
t.Skip("test only with postgres")
|
||||
}
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
coordID := uuid.New()
|
||||
_, err := store.UpsertTailnetCoordinator(ctx, coordID)
|
||||
require.NoError(t, err)
|
||||
|
||||
peerID := uuid.New()
|
||||
node := &proto.Node{PreferredDerp: 44}
|
||||
nodeb, err := gProto.Marshal(node)
|
||||
require.NoError(t, err)
|
||||
_, err = store.UpsertTailnetPeer(ctx, database.UpsertTailnetPeerParams{
|
||||
ID: peerID,
|
||||
CoordinatorID: coordID,
|
||||
Node: nodeb,
|
||||
Status: database.TailnetStatusLost,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
dstID := uuid.New()
|
||||
_, err = store.UpsertTailnetTunnel(ctx, database.UpsertTailnetTunnelParams{
|
||||
CoordinatorID: coordID,
|
||||
SrcID: peerID,
|
||||
DstID: dstID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
debug, err := getDebug(ctx, store)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, debug.Coordinators, 1)
|
||||
require.Len(t, debug.Peers, 1)
|
||||
require.Len(t, debug.Tunnels, 1)
|
||||
|
||||
require.Equal(t, coordID, debug.Coordinators[0].ID)
|
||||
|
||||
require.Equal(t, peerID, debug.Peers[0].ID)
|
||||
require.Equal(t, coordID, debug.Peers[0].CoordinatorID)
|
||||
require.Equal(t, database.TailnetStatusLost, debug.Peers[0].Status)
|
||||
require.Equal(t, node.String(), debug.Peers[0].Node)
|
||||
|
||||
require.Equal(t, coordID, debug.Tunnels[0].CoordinatorID)
|
||||
require.Equal(t, peerID, debug.Tunnels[0].SrcID)
|
||||
require.Equal(t, dstID, debug.Tunnels[0].DstID)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
th, td {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
tr {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id=coordinators><a href=#coordinators>#</a> coordinators: total 2</h2>
|
||||
<table>
|
||||
<tr style="margin-top:4px">
|
||||
<th>ID</th>
|
||||
<th>Heartbeat Age</th>
|
||||
</tr>
|
||||
<tr style="margin-top:4px">
|
||||
<td>01000000-1111-1111-1111-111111111111</td>
|
||||
<td>2s ago</td>
|
||||
</tr>
|
||||
<tr style="margin-top:4px">
|
||||
<td>02000000-1111-1111-1111-111111111111</td>
|
||||
<td>1s ago</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2 id=peers> <a href=#peers>#</a> peers: total 2 </h2>
|
||||
<table>
|
||||
<tr style="margin-top:4px">
|
||||
<th>ID</th>
|
||||
<th>CoordinatorID</th>
|
||||
<th>Status</th>
|
||||
<th>Last Write Age</th>
|
||||
<th>Node</th>
|
||||
</tr>
|
||||
<tr style="margin-top:4px">
|
||||
<td>01000000-2222-2222-2222-222222222222</td>
|
||||
<td>01000000-1111-1111-1111-111111111111</td>
|
||||
<td>ok</td>
|
||||
<td>5s ago</td>
|
||||
<td style="white-space: pre;"><code>id:1 preferred_derp:999 endpoints:"192.168.0.49:4449"</code></td>
|
||||
</tr>
|
||||
<tr style="margin-top:4px">
|
||||
<td>02000000-2222-2222-2222-222222222222</td>
|
||||
<td>02000000-1111-1111-1111-111111111111</td>
|
||||
<td>lost</td>
|
||||
<td>7s ago</td>
|
||||
<td style="white-space: pre;"><code>id:2 preferred_derp:999 endpoints:"192.168.0.33:4449"</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2 id=tunnels><a href=#tunnels>#</a> tunnels: total 1</h2>
|
||||
<table>
|
||||
<tr style="margin-top:4px">
|
||||
<th>SrcID</th>
|
||||
<th>DstID</th>
|
||||
<th>CoordinatorID</th>
|
||||
<th>Last Write Age</th>
|
||||
</tr>
|
||||
<tr style="margin-top:4px">
|
||||
<td>01000000-2222-2222-2222-222222222222</td>
|
||||
<td>02000000-2222-2222-2222-222222222222</td>
|
||||
<td>01000000-1111-1111-1111-111111111111</td>
|
||||
<td>3s ago</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -82,6 +82,7 @@ result
|
|||
|
||||
# Testdata shouldn't be formatted.
|
||||
../scripts/apitypings/testdata/**/*.ts
|
||||
../enterprise/tailnet/testdata/*.golden.html
|
||||
|
||||
# Generated files shouldn't be formatted.
|
||||
e2e/provisionerGenerated.ts
|
||||
|
|
|
@ -82,6 +82,7 @@ result
|
|||
|
||||
# Testdata shouldn't be formatted.
|
||||
../scripts/apitypings/testdata/**/*.ts
|
||||
../enterprise/tailnet/testdata/*.golden.html
|
||||
|
||||
# Generated files shouldn't be formatted.
|
||||
e2e/provisionerGenerated.ts
|
||||
|
|
Loading…
Reference in New Issue