mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(cli): ensure that the support bundle command does not panic on zero values (#14392)
We try to write a cute little summary at the end of the bundle, but that could panic if some of the fields of the bundle were nil. Adds a test that essentially ensures nil values in a bundle, and ensures that it can be handled without losing our towels. Co-authored-by: Danny Kopping <danny@coder.com>
This commit is contained in:
co-authored by
Danny Kopping
parent
3514ca3476
commit
82e6070c7a
+35
-9
@@ -184,16 +184,8 @@ func (r *RootCmd) supportBundle() *serpent.Command {
|
||||
_ = os.Remove(outputPath) // best effort
|
||||
return xerrors.Errorf("create support bundle: %w", err)
|
||||
}
|
||||
docsURL := bun.Deployment.Config.Values.DocsURL.String()
|
||||
deployHealthSummary := bun.Deployment.HealthReport.Summarize(docsURL)
|
||||
if len(deployHealthSummary) > 0 {
|
||||
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
|
||||
}
|
||||
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:", docsURL)
|
||||
if len(clientNetcheckSummary) > 0 {
|
||||
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
|
||||
}
|
||||
|
||||
summarizeBundle(inv, bun)
|
||||
bun.CLILogs = cliLogBuf.Bytes()
|
||||
|
||||
if err := writeBundle(bun, zwr); err != nil {
|
||||
@@ -225,6 +217,40 @@ func (r *RootCmd) supportBundle() *serpent.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// summarizeBundle makes a best-effort attempt to write a short summary
|
||||
// of the support bundle to the user's terminal.
|
||||
func summarizeBundle(inv *serpent.Invocation, bun *support.Bundle) {
|
||||
if bun == nil {
|
||||
cliui.Error(inv.Stdout, "No support bundle generated!")
|
||||
return
|
||||
}
|
||||
|
||||
if bun.Deployment.Config == nil {
|
||||
cliui.Error(inv.Stdout, "No deployment configuration available!")
|
||||
return
|
||||
}
|
||||
|
||||
docsURL := bun.Deployment.Config.Values.DocsURL.String()
|
||||
if bun.Deployment.HealthReport == nil {
|
||||
cliui.Error(inv.Stdout, "No deployment health report available!")
|
||||
return
|
||||
}
|
||||
deployHealthSummary := bun.Deployment.HealthReport.Summarize(docsURL)
|
||||
if len(deployHealthSummary) > 0 {
|
||||
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
|
||||
}
|
||||
|
||||
if bun.Network.Netcheck == nil {
|
||||
cliui.Error(inv.Stdout, "No network troubleshooting information available!")
|
||||
return
|
||||
}
|
||||
|
||||
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:", docsURL)
|
||||
if len(clientNetcheckSummary) > 0 {
|
||||
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
|
||||
}
|
||||
}
|
||||
|
||||
func findAgent(agentName string, haystack []codersdk.WorkspaceResource) (*codersdk.WorkspaceAgent, bool) {
|
||||
for _, res := range haystack {
|
||||
for _, agt := range res.Agents {
|
||||
|
||||
@@ -5,6 +5,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -14,6 +17,7 @@ import (
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/agent"
|
||||
@@ -156,6 +160,53 @@ func TestSupportBundle(t *testing.T) {
|
||||
err := inv.Run()
|
||||
require.ErrorContains(t, err, "failed authorization check")
|
||||
})
|
||||
|
||||
// This ensures that the CLI does not panic when trying to generate a support bundle
|
||||
// against a fake server that returns an empty response for all requests. This essentially
|
||||
// ensures that (almost) all of the support bundle generating code paths get a zero value.
|
||||
t.Run("DontPanic", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, code := range []int{
|
||||
http.StatusOK,
|
||||
http.StatusUnauthorized,
|
||||
http.StatusForbidden,
|
||||
http.StatusNotFound,
|
||||
http.StatusInternalServerError,
|
||||
} {
|
||||
t.Run(http.StatusText(code), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Start up a fake server
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Logf("received request: %s %s", r.Method, r.URL)
|
||||
switch r.URL.Path {
|
||||
case "/api/v2/authcheck":
|
||||
// Fake auth check
|
||||
resp := codersdk.AuthorizationResponse{
|
||||
"Read DeploymentValues": true,
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
assert.NoError(t, json.NewEncoder(w).Encode(resp))
|
||||
default:
|
||||
// Simply return a blank response for everything else.
|
||||
w.WriteHeader(code)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
u, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
client := codersdk.New(u)
|
||||
|
||||
d := t.TempDir()
|
||||
path := filepath.Join(d, "bundle.zip")
|
||||
|
||||
inv, root := clitest.New(t, "support", "bundle", "--url-override", srv.URL, "--output-file", path, "--yes")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// nolint:revive // It's a control flag, but this is just a test.
|
||||
|
||||
Reference in New Issue
Block a user