8000 feat(cli): add hidden netcheck command (#8136) · coder/coder@bc739bd · GitHub
[go: up one dir, main page]

Skip to content

Commit bc739bd

Browse files
authored
feat(cli): add hidden netcheck command (#8136)
1 parent 7703bb7 commit bc739bd

File tree

8 files changed

+189
-0
lines changed

8 files changed

+189
-0
lines changed

cli/netcheck.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"time"
8+
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/cli/clibase"
12+
"github.com/coder/coder/coderd/healthcheck"
13+
"github.com/coder/coder/codersdk"
14+
)
15+
16+
func (r *RootCmd) netcheck() *clibase.Cmd {
17+
client := new(codersdk.Client)
18+
19+
cmd := &clibase.Cmd{
20+
Use: "netcheck",
21+
Short: "Print network debug information for DERP and STUN",
22+
Hidden: true,
23+
Middleware: clibase.Chain(
24+
r.InitClient(client),
25+
),
26+
Handler: func(inv *clibase.Invocation) error {
27+
ctx, cancel := context.WithTimeout(inv.Context(), 30*time.Second)
28+
defer cancel()
29+
30+
connInfo, err := client.WorkspaceAgentConnectionInfo(ctx)
31+
if err != nil {
32+
return err
33+
}
34+
35+
_, _ = fmt.Fprint(inv.Stderr, "Gathering a network report. This may take a few seconds...\n\n")
36+
37+
var report healthcheck.DERPReport
38+
report.Run(ctx, &healthcheck.DERPReportOptions{
39+
DERPMap: connInfo.DERPMap,
40+
})
41+
42+
raw, err := json.MarshalIndent(report, "", " ")
43+
if err != nil {
44+
return err
45+
}
46+
47+
n, err := inv.Stdout.Write(raw)
48+
if err != nil {
49+
return err
50+
}
51+
if n != len(raw) {
52+
return xerrors.Errorf("failed to write all bytes to stdout; wrote %d, len %d", n, len(raw))
53+
}
54+
55+
_, _ = inv.Stdout.Write([]byte("\n"))
56+
return nil
57+
},
58+
}
59+
60+
cmd.Options = clibase.OptionSet{}
61+
return cmd
62+
}

cli/netcheck_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package cli_test
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/coder/coder/cli/clitest"
12+
"github.com/coder/coder/coderd/healthcheck"
13+
"github.com/coder/coder/pty/ptytest"
14+
)
15+
16+
func TestNetcheck(t *testing.T) {
17+
t.Parallel()
18+
19+
pty := ptytest.New(t)
20+
config := login(t, pty)
21+
22+
var out bytes.Buffer
23+
inv, _ := clitest.New(t, "netcheck", "--global-config", string(config))
24+
inv.Stdout = &out
25+
26+
clitest.StartWithWaiter(t, inv).RequireSuccess()
27+
28+
var report healthcheck.DERPReport
29+
require.NoError(t, json.Unmarshal(out.Bytes(), &report))
30+
31+
assert.True(t, report.Healthy)
32+
require.Len(t, report.Regions, 1)
33+
require.Len(t, report.Regions[1].NodeReports, 1)
34+
}

cli/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func (r *RootCmd) Core() []*clibase.Cmd {
107107

108108
// Hidden
109109
r.gitssh(),
110+
r.netcheck(),
110111
r.vscodeSSH(),
111112
r.workspaceAgent(),
112113
}

coderd/apidoc/docs.go

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ func New(options *Options) *API {
672672
r.Post("/azure-instance-identity", api.postWorkspaceAuthAzureInstanceIdentity)
673673
r.Post("/aws-instance-identity", api.postWorkspaceAuthAWSInstanceIdentity)
674674
r.Post("/google-instance-identity", api.postWorkspaceAuthGoogleInstanceIdentity)
675+
r.Get("/connection", api.workspaceAgentConnectionGeneric)
675676
r.Route("/me", func(r chi.Router) {
676677
r.Use(httpmw.ExtractWorkspaceAgent(options.Database))
677678
r.Get("/manifest", api.workspaceAgentManifest)

coderd/workspaceagents.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,25 @@ func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request
805805
})
806806
}
807807

808+
// workspaceAgentConnectionGeneric is the same as workspaceAgentConnection but
809+
// without the workspaceagent path parameter.
810+
//
811+
// @Summary Get connection info for workspace agent generic
812+
// @ID get-connection-info-for-workspace-agent-generic
813+
// @Security CoderSessionToken
814+
// @Produce json
815+
// @Tags Agents
816+
// @Success 200 {object} codersdk.WorkspaceAgentConnectionInfo
817+
// @Router /workspaceagents/connection [get]
818+
// @x-apidocgen {"skip": true}
819+
func (api *API) workspaceAgentConnectionGeneric(rw http.ResponseWriter, r *http.Request) {
820+
ctx := r.Context()
821+
822+
httpapi.Write(ctx, rw, http.StatusOK, codersdk.WorkspaceAgentConnectionInfo{
823+
DERPMap: api.DERPMap,
824+
})
825+
}
826+
808827
// @Summary Coordinate workspace agent via Tailnet
809828
// @Description It accepts a WebSocket connection to an agent that listens to
810829
// @Description incoming connections and publishes node updates.

codersdk/workspaceagents.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,26 @@ type WorkspaceAgentConnectionInfo struct {
170170
DERPMap *tailcfg.DERPMap `json:"derp_map"`
171171
}
172172

173+
func (c *Client) WorkspaceAgentConnectionInfo(ctx context.Context) (*WorkspaceAgentConnectionInfo, error) {
174+
res, err := c.Request(ctx, http.MethodGet, "/api/v2/workspaceagents/connection", nil)
175+
if err != nil {
176+
return nil, err
177+
}
178+
defer res.Body.Close()
179+
180+
if res.StatusCode != http.StatusOK {
181+
return nil, ReadBodyAsError(res)
182+
}
183+
184+
var info WorkspaceAgentConnectionInfo
185+
err = json.NewDecoder(res.Body).Decode(&info)
186+
if err != nil {
187+
return nil, xerrors.Errorf("decode connection info: %w", err)
188+
}
189+
190+
return &info, nil
191+
}
192+
173193
// @typescript-ignore DialWorkspaceAgentOptions
174194
type DialWorkspaceAgentOptions struct {
175195
Logger slog.Logger

0 commit comments

Comments
 (0)
0