Skip to content

Commit 2abc1cd

Browse files
authored
feat(support): fetch agent network info over tailnet (#12577)
Adds agent-related information to support bundle command.
1 parent 653ddcc commit 2abc1cd

File tree

4 files changed

+265
-90
lines changed

4 files changed

+265
-90
lines changed

cli/support.go

+16-9
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,19 @@ func findAgent(agentName string, haystack []codersdk.WorkspaceResource) (*coders
137137
}
138138

139139
func writeBundle(src *support.Bundle, dest *zip.Writer) error {
140+
// We JSON-encode the following:
140141
for k, v := range map[string]any{
141142
"deployment/buildinfo.json": src.Deployment.BuildInfo,
142143
"deployment/config.json": src.Deployment.Config,
143144
"deployment/experiments.json": src.Deployment.Experiments,
144145
"deployment/health.json": src.Deployment.HealthReport,
145-
"network/netcheck_local.json": src.Network.NetcheckLocal,
146-
"network/netcheck_remote.json": src.Network.NetcheckRemote,
146+
"network/netcheck.json": src.Network.Netcheck,
147147
"workspace/workspace.json": src.Workspace.Workspace,
148-
"workspace/agent.json": src.Workspace.Agent,
148+
"agent/agent.json": src.Agent.Agent,
149+
"agent/listening_ports.json": src.Agent.ListeningPorts,
150+
"agent/manifest.json": src.Agent.Manifest,
151+
"agent/peer_diagnostics.json": src.Agent.PeerDiagnostics,
152+
"agent/ping_result.json": src.Agent.PingResult,
149153
"workspace/template.json": src.Workspace.Template,
150154
"workspace/template_version.json": src.Workspace.TemplateVersion,
151155
"workspace/parameters.json": src.Workspace.Parameters,
@@ -166,13 +170,16 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
166170
return xerrors.Errorf("decode template zip from base64")
167171
}
168172

173+
// The below we just write as we have them:
169174
for k, v := range map[string]string{
170-
"network/coordinator_debug.html": src.Network.CoordinatorDebug,
171-
"network/tailnet_debug.html": src.Network.TailnetDebug,
172-
"workspace/build_logs.txt": humanizeBuildLogs(src.Workspace.BuildLogs),
173-
"workspace/agent_startup_logs.txt": humanizeAgentLogs(src.Workspace.AgentStartupLogs),
174-
"workspace/template_file.zip": string(templateVersionBytes),
175-
"logs.txt": strings.Join(src.Logs, "\n"),
175+
"network/coordinator_debug.html": src.Network.CoordinatorDebug,
176+
"network/tailnet_debug.html": src.Network.TailnetDebug,
177+
"workspace/build_logs.txt": humanizeBuildLogs(src.Workspace.BuildLogs),
178+
"agent/logs.txt": string(src.Agent.Logs),
179+
"agent/magicsock.html": string(src.Agent.MagicsockHTML),
180+
"agent/startup_logs.txt": humanizeAgentLogs(src.Agent.StartupLogs),
181+
"workspace/template_file.zip": string(templateVersionBytes),
182+
"logs.txt": strings.Join(src.Logs, "\n"),
176183
} {
177184
f, err := dest.Create(k)
178185
if err != nil {

cli/support_test.go

+45-9
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,26 @@ import (
44
"archive/zip"
55
"encoding/json"
66
"io"
7+
"os"
78
"path/filepath"
89
"runtime"
910
"testing"
1011
"time"
1112

13+
"tailscale.com/ipn/ipnstate"
14+
1215
"github.com/stretchr/testify/require"
1316

17+
"github.com/coder/coder/v2/agent"
18+
"github.com/coder/coder/v2/agent/agenttest"
1419
"github.com/coder/coder/v2/cli/clitest"
1520
"github.com/coder/coder/v2/coderd/coderdtest"
1621
"github.com/coder/coder/v2/coderd/database"
1722
"github.com/coder/coder/v2/coderd/database/dbfake"
1823
"github.com/coder/coder/v2/coderd/database/dbtime"
1924
"github.com/coder/coder/v2/codersdk"
25+
"github.com/coder/coder/v2/codersdk/agentsdk"
26+
"github.com/coder/coder/v2/tailnet"
2027
"github.com/coder/coder/v2/testutil"
2128
)
2229

@@ -37,7 +44,14 @@ func TestSupportBundle(t *testing.T) {
3744
}).WithAgent().Do()
3845
ws, err := client.Workspace(ctx, r.Workspace.ID)
3946
require.NoError(t, err)
40-
agt := ws.LatestBuild.Resources[0].Agents[0]
47+
tempDir := t.TempDir()
48+
logPath := filepath.Join(tempDir, "coder-agent.log")
49+
require.NoError(t, os.WriteFile(logPath, []byte("hello from the agent"), 0o600))
50+
agt := agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
51+
o.LogDir = tempDir
52+
})
53+
defer agt.Close()
54+
coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
4155

4256
// Insert a provisioner job log
4357
_, err = db.InsertProvisionerJobLogs(ctx, database.InsertProvisionerJobLogsParams{
@@ -51,7 +65,7 @@ func TestSupportBundle(t *testing.T) {
5165
require.NoError(t, err)
5266
// Insert an agent log
5367
_, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
54-
AgentID: agt.ID,
68+
AgentID: ws.LatestBuild.Resources[0].Agents[0].ID,
5569
CreatedAt: dbtime.Now(),
5670
Output: []string{"started up"},
5771
Level: []database.LogLevel{database.LogLevelInfo},
@@ -141,22 +155,44 @@ func assertBundleContents(t *testing.T, path string) {
141155
case "network/tailnet_debug.html":
142156
bs := readBytesFromZip(t, f)
143157
require.NotEmpty(t, bs, "tailnet debug should not be empty")
144-
case "network/netcheck_local.json", "network/netcheck_remote.json":
145-
// TODO: setup fake agent?
146-
bs := readBytesFromZip(t, f)
147-
require.NotEmpty(t, bs, "netcheck should not be empty")
158+
case "network/netcheck.json":
159+
var v codersdk.WorkspaceAgentConnectionInfo
160+
decodeJSONFromZip(t, f, &v)
161+
require.NotEmpty(t, v, "connection info should not be empty")
148162
case "workspace/workspace.json":
149163
var v codersdk.Workspace
150164
decodeJSONFromZip(t, f, &v)
151165
require.NotEmpty(t, v, "workspace should not be empty")
152166
case "workspace/build_logs.txt":
153167
bs := readBytesFromZip(t, f)
154168
require.Contains(t, string(bs), "provision done")
155-
case "workspace/agent.json":
169+
case "agent/agent.json":
156170
var v codersdk.WorkspaceAgent
157171
decodeJSONFromZip(t, f, &v)
158172
require.NotEmpty(t, v, "agent should not be empty")
159-
case "workspace/agent_startup_logs.txt":
173+
case "agent/listening_ports.json":
174+
var v codersdk.WorkspaceAgentListeningPortsResponse
175+
decodeJSONFromZip(t, f, &v)
176+
require.NotEmpty(t, v, "agent listening ports should not be empty")
177+
case "agent/logs.txt":
178+
bs := readBytesFromZip(t, f)
179+
require.NotEmpty(t, bs, "logs should not be empty")
180+
case "agent/magicsock.html":
181+
bs := readBytesFromZip(t, f)
182+
require.NotEmpty(t, bs, "agent magicsock should not be empty")
183+
case "agent/manifest.json":
184+
var v agentsdk.Manifest
185+
decodeJSONFromZip(t, f, &v)
186+
require.NotEmpty(t, v, "agent manifest should not be empty")
187+
case "agent/peer_diagnostics.json":
188+
var v *tailnet.PeerDiagnostics
189+
decodeJSONFromZip(t, f, &v)
190+
require.NotEmpty(t, v, "peer diagnostics should not be empty")
191+
case "agent/ping_result.json":
192+
var v *ipnstate.PingResult
193+
decodeJSONFromZip(t, f, &v)
194+
require.NotEmpty(t, v, "ping result should not be empty")
195+
case "agent/startup_logs.txt":
160196
bs := readBytesFromZip(t, f)
161197
require.Contains(t, string(bs), "started up")
162198
case "workspace/template.json":
@@ -178,7 +214,7 @@ func assertBundleContents(t *testing.T, path string) {
178214
bs := readBytesFromZip(t, f)
179215
require.NotEmpty(t, bs, "logs should not be empty")
180216
default:
181-
require.Failf(t, "unexpected file in bundle: %q", f.Name)
217+
require.Failf(t, "unexpected file in bundle", f.Name)
182218
}
183219
}
184220
}

0 commit comments

Comments
 (0)