Skip to content

Commit 157164c

Browse files
committed
fix(cli): ensure that the support bundle command does not panic on zero values
1 parent 3514ca3 commit 157164c

File tree

2 files changed

+76
-9
lines changed

2 files changed

+76
-9
lines changed

cli/support.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,8 @@ func (r *RootCmd) supportBundle() *serpent.Command {
184184
_ = os.Remove(outputPath) // best effort
185185
return xerrors.Errorf("create support bundle: %w", err)
186186
}
187-
docsURL := bun.Deployment.Config.Values.DocsURL.String()
188-
deployHealthSummary := bun.Deployment.HealthReport.Summarize(docsURL)
189-
if len(deployHealthSummary) > 0 {
190-
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
191-
}
192-
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:", docsURL)
193-
if len(clientNetcheckSummary) > 0 {
194-
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
195-
}
196187

188+
summarizeBundle(inv, bun)
197189
bun.CLILogs = cliLogBuf.Bytes()
198190

199191
if err := writeBundle(bun, zwr); err != nil {
@@ -225,6 +217,41 @@ func (r *RootCmd) supportBundle() *serpent.Command {
225217
return cmd
226218
}
227219

220+
// summarizeBundle makes a best-effort attempt to write a short summary
221+
// of the support bundle to the user's terminal.
222+
func summarizeBundle(inv *serpent.Invocation, bun *support.Bundle) {
223+
if bun == nil {
224+
cliui.Error(inv.Stdout, "No support bundle generated!")
225+
return
226+
}
227+
228+
if bun.Deployment.Config == nil {
229+
cliui.Error(inv.Stdout, "No deployment configuration available!")
230+
return
231+
}
232+
233+
docsURL := bun.Deployment.Config.Values.DocsURL.String()
234+
if bun.Deployment.HealthReport == nil {
235+
cliui.Error(inv.Stdout, "No deployment health report available!")
236+
return
237+
}
238+
239+
deployHealthSummary := bun.Deployment.HealthReport.Summarize(docsURL)
240+
if len(deployHealthSummary) > 0 {
241+
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
242+
}
243+
244+
if bun.Network.Netcheck == nil {
245+
cliui.Error(inv.Stdout, "No network troubleshooting information available!")
246+
return
247+
}
248+
249+
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:", docsURL)
250+
if len(clientNetcheckSummary) > 0 {
251+
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
252+
}
253+
}
254+
228255
func findAgent(agentName string, haystack []codersdk.WorkspaceResource) (*codersdk.WorkspaceAgent, bool) {
229256
for _, res := range haystack {
230257
for _, agt := range res.Agents {

cli/support_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"bytes"
66
"encoding/json"
77
"io"
8+
"net/http"
9+
"net/http/httptest"
10+
"net/url"
811
"os"
912
"path/filepath"
1013
"runtime"
@@ -14,6 +17,7 @@ import (
1417
"tailscale.com/ipn/ipnstate"
1518

1619
"github.com/google/uuid"
20+
"github.com/stretchr/testify/assert"
1721
"github.com/stretchr/testify/require"
1822

1923
"github.com/coder/coder/v2/agent"
@@ -156,6 +160,42 @@ func TestSupportBundle(t *testing.T) {
156160
err := inv.Run()
157161
require.ErrorContains(t, err, "failed authorization check")
158162
})
163+
164+
// This ensures that the CLI does not panic when trying to generate a support bundle
165+
// against a fake server that returns a 200 OK for all requests. This essentially
166+
// ensures that (almost) all of the support bundle generating code paths get a zero value.
167+
t.Run("DontPanic", func(t *testing.T) {
168+
t.Parallel()
169+
170+
// Start up a fake server that will return a blank 200 OK response for everything.
171+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
172+
t.Logf("received request: %s %s", r.Method, r.URL)
173+
switch r.URL.Path {
174+
case "/api/v2/authcheck":
175+
// Fake auth check
176+
resp := codersdk.AuthorizationResponse{
177+
"Read DeploymentValues": true,
178+
}
179+
w.WriteHeader(http.StatusOK)
180+
assert.NoError(t, json.NewEncoder(w).Encode(resp))
181+
default:
182+
// Simply return a 200 OK for everything else.
183+
w.WriteHeader(http.StatusOK)
184+
}
185+
}))
186+
u, err := url.Parse(srv.URL)
187+
require.NoError(t, err)
188+
client := codersdk.New(u)
189+
defer srv.Close()
190+
191+
d := t.TempDir()
192+
path := filepath.Join(d, "bundle.zip")
193+
194+
inv, root := clitest.New(t, "support", "bundle", "--url-override", srv.URL, "--output-file", path, "--yes")
195+
clitest.SetupConfig(t, client, root)
196+
err = inv.Run()
197+
require.NoError(t, err)
198+
})
159199
}
160200

161201
// nolint:revive // It's a control flag, but this is just a test.

0 commit comments

Comments
 (0)