Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf2a9e51

Browse files
authored
feat(cli/support): confirm before creating bundle (#12684)
Forces user to confirm before creating a support bundle.Also adds contextual information to the bundle under cli_logs.txt.
1 parent8ea5fb7 commitf2a9e51

File tree

3 files changed

+107
-18
lines changed

3 files changed

+107
-18
lines changed

‎cli/support.go

Lines changed: 99 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/base64"
77
"encoding/json"
88
"fmt"
9+
"net/url"
910
"os"
1011
"path/filepath"
1112
"strings"
@@ -16,6 +17,7 @@ import (
1617

1718
"cdr.dev/slog"
1819
"cdr.dev/slog/sloggers/sloghuman"
20+
"github.com/coder/coder/v2/cli/cliui"
1921
"github.com/coder/coder/v2/codersdk"
2022
"github.com/coder/coder/v2/support"
2123
"github.com/coder/serpent"
@@ -36,8 +38,26 @@ func (r *RootCmd) support() *serpent.Command {
3638
returnsupportCmd
3739
}
3840

41+
varsupportBundleBlurb=cliui.Bold("This will collect the following information:\n")+
42+
` - Coder deployment version
43+
- Coder deployment Configuration (sanitized), including enabled experiments
44+
- Coder deployment health snapshot
45+
- Coder deployment Network troubleshooting information
46+
- Workspace configuration, parameters, and build logs
47+
- Template version and source code for the given workspace
48+
- Agent details (with environment variable sanitized)
49+
- Agent network diagnostics
50+
- Agent logs
51+
`+cliui.Bold("Note: ")+
52+
cliui.Wrap(`While we try to sanitize sensitive data from support bundles, we cannot guarantee that they do not contain information that you or your organization may consider sensitive.\n`)+
53+
cliui.Bold("Please confirm that you will:\n")+
54+
" - Review the support bundle before distribution\n"+
55+
" - Only distribute it via trusted channels\n"+
56+
cliui.Bold("Continue? ")
57+
3958
func (r*RootCmd)supportBundle()*serpent.Command {
4059
varoutputPathstring
60+
varcoderURLOverridestring
4161
client:=new(codersdk.Client)
4262
cmd:=&serpent.Command{
4363
Use:"bundle <workspace> [<agent>]",
@@ -48,14 +68,52 @@ func (r *RootCmd) supportBundle() *serpent.Command {
4868
r.InitClient(client),
4969
),
5070
Handler:func(inv*serpent.Invocation)error {
51-
var (
52-
log=slog.Make(sloghuman.Sink(inv.Stderr)).
53-
Leveled(slog.LevelDebug)
54-
deps= support.Deps{
55-
Client:client,
56-
Log:log,
57-
}
71+
varcliLogBuf bytes.Buffer
72+
cliLogW:=sloghuman.Sink(&cliLogBuf)
73+
cliLog:=slog.Make(cliLogW).Leveled(slog.LevelDebug)
74+
ifr.verbose {
75+
cliLog=cliLog.AppendSinks(sloghuman.Sink(inv.Stderr))
76+
}
77+
ans,err:=cliui.Prompt(inv, cliui.PromptOptions{
78+
Text:supportBundleBlurb,
79+
Secret:false,
80+
IsConfirm:true,
81+
})
82+
iferr!=nil||ans!=cliui.ConfirmYes {
83+
returnerr
84+
}
85+
ifskip,_:=inv.ParsedFlags().GetBool("yes");skip {
86+
cliLog.Debug(inv.Context(),"user auto-confirmed")
87+
}else {
88+
cliLog.Debug(inv.Context(),"user confirmed manually",slog.F("answer",ans))
89+
}
90+
91+
vi:=defaultVersionInfo()
92+
cliLog.Debug(inv.Context(),"version info",
93+
slog.F("version",vi.Version),
94+
slog.F("build_time",vi.BuildTime),
95+
slog.F("external_url",vi.ExternalURL),
96+
slog.F("slim",vi.Slim),
97+
slog.F("agpl",vi.AGPL),
98+
slog.F("boring_crypto",vi.BoringCrypto),
5899
)
100+
cliLog.Debug(inv.Context(),"invocation",slog.F("args",strings.Join(os.Args," ")))
101+
102+
// Check if we're running inside a workspace
103+
ifval,found:=os.LookupEnv("CODER");found&&val=="true" {
104+
_,_=fmt.Fprintln(inv.Stderr,"Running inside Coder workspace; this can affect results!")
105+
cliLog.Debug(inv.Context(),"running inside coder workspace")
106+
}
107+
108+
ifcoderURLOverride!=""&&coderURLOverride!=client.URL.String() {
109+
u,err:=url.Parse(coderURLOverride)
110+
iferr!=nil {
111+
returnxerrors.Errorf("invalid value for Coder URL override: %w",err)
112+
}
113+
_,_=fmt.Fprintf(inv.Stderr,"Overrode Coder URL to %q; this can affect results!\n",coderURLOverride)
114+
cliLog.Debug(inv.Context(),"coder url overridden",slog.F("url",coderURLOverride))
115+
client.URL=u
116+
}
59117

60118
iflen(inv.Args)==0 {
61119
returnxerrors.Errorf("must specify workspace name")
@@ -64,8 +122,10 @@ func (r *RootCmd) supportBundle() *serpent.Command {
64122
iferr!=nil {
65123
returnxerrors.Errorf("invalid workspace: %w",err)
66124
}
67-
68-
deps.WorkspaceID=ws.ID
125+
cliLog.Debug(inv.Context(),"found workspace",
126+
slog.F("workspace_name",ws.Name),
127+
slog.F("workspace_id",ws.ID),
128+
)
69129

70130
agentName:=""
71131
iflen(inv.Args)>1 {
@@ -76,8 +136,10 @@ func (r *RootCmd) supportBundle() *serpent.Command {
76136
if!found {
77137
returnxerrors.Errorf("could not find agent named %q for workspace",agentName)
78138
}
79-
80-
deps.AgentID=agt.ID
139+
cliLog.Debug(inv.Context(),"found workspace agent",
140+
slog.F("agent_name",agt.Name),
141+
slog.F("agent_id",agt.ID),
142+
)
81143

82144
ifoutputPath=="" {
83145
cwd,err:=filepath.Abs(".")
@@ -87,6 +149,7 @@ func (r *RootCmd) supportBundle() *serpent.Command {
87149
fname:=fmt.Sprintf("coder-support-%d.zip",time.Now().Unix())
88150
outputPath=filepath.Join(cwd,fname)
89151
}
152+
cliLog.Debug(inv.Context(),"output path",slog.F("path",outputPath))
90153

91154
w,err:=os.Create(outputPath)
92155
iferr!=nil {
@@ -95,27 +158,48 @@ func (r *RootCmd) supportBundle() *serpent.Command {
95158
zwr:=zip.NewWriter(w)
96159
deferzwr.Close()
97160

161+
clientLog:=slog.Make().Leveled(slog.LevelDebug)
162+
ifr.verbose {
163+
clientLog.AppendSinks(sloghuman.Sink(inv.Stderr))
164+
}
165+
deps:= support.Deps{
166+
Client:client,
167+
// Support adds a sink so we don't need to supply one ourselves.
168+
Log:clientLog,
169+
WorkspaceID:ws.ID,
170+
AgentID:agt.ID,
171+
}
172+
98173
bun,err:=support.Run(inv.Context(),&deps)
99174
iferr!=nil {
100175
_=os.Remove(outputPath)// best effort
101176
returnxerrors.Errorf("create support bundle: %w",err)
102177
}
178+
bun.CLILogs=cliLogBuf.Bytes()
103179

104180
iferr:=writeBundle(bun,zwr);err!=nil {
105181
_=os.Remove(outputPath)// best effort
106182
returnxerrors.Errorf("write support bundle to %s: %w",outputPath,err)
107183
}
184+
_,_=fmt.Fprintln(inv.Stderr,"Wrote support bundle to "+outputPath)
108185
returnnil
109186
},
110187
}
111188
cmd.Options= serpent.OptionSet{
189+
cliui.SkipPromptOption(),
112190
{
113-
Flag:"output",
114-
FlagShorthand:"o",
115-
Env:"CODER_SUPPORT_BUNDLE_OUTPUT",
191+
Flag:"output-file",
192+
FlagShorthand:"O",
193+
Env:"CODER_SUPPORT_BUNDLE_OUTPUT_FILE",
116194
Description:"File path for writing the generated support bundle. Defaults to coder-support-$(date +%s).zip.",
117195
Value:serpent.StringOf(&outputPath),
118196
},
197+
{
198+
Flag:"url-override",
199+
Env:"CODER_SUPPORT_BUNDLE_URL_OVERRIDE",
200+
Description:"Override the URL to your Coder deployment. This may be useful, for example, if you need to troubleshoot a specific Coder replica.",
201+
Value:serpent.StringOf(&coderURLOverride),
202+
},
119203
}
120204

121205
returncmd
@@ -182,6 +266,7 @@ func writeBundle(src *support.Bundle, dest *zip.Writer) error {
182266
"agent/prometheus.txt":string(src.Agent.Prometheus),
183267
"workspace/template_file.zip":string(templateVersionBytes),
184268
"logs.txt":strings.Join(src.Logs,"\n"),
269+
"cli_logs.txt":string(src.CLILogs),
185270
} {
186271
f,err:=dest.Create(k)
187272
iferr!=nil {

‎cli/support_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func TestSupportBundle(t *testing.T) {
7676

7777
d:=t.TempDir()
7878
path:=filepath.Join(d,"bundle.zip")
79-
inv,root:=clitest.New(t,"support","bundle",r.Workspace.Name,"--output",path)
79+
inv,root:=clitest.New(t,"support","bundle",r.Workspace.Name,"--output-file",path,"--yes")
8080
//nolint: gocritic // requires owner privilege
8181
clitest.SetupConfig(t,client,root)
8282
err=inv.Run()
@@ -88,7 +88,7 @@ func TestSupportBundle(t *testing.T) {
8888
t.Parallel()
8989
client:=coderdtest.New(t,nil)
9090
_=coderdtest.CreateFirstUser(t,client)
91-
inv,root:=clitest.New(t,"support","bundle")
91+
inv,root:=clitest.New(t,"support","bundle","--yes")
9292
//nolint: gocritic // requires owner privilege
9393
clitest.SetupConfig(t,client,root)
9494
err:=inv.Run()
@@ -103,7 +103,7 @@ func TestSupportBundle(t *testing.T) {
103103
OrganizationID:admin.OrganizationID,
104104
OwnerID:admin.UserID,
105105
}).Do()// without agent!
106-
inv,root:=clitest.New(t,"support","bundle",r.Workspace.Name)
106+
inv,root:=clitest.New(t,"support","bundle",r.Workspace.Name,"--yes")
107107
//nolint: gocritic // requires owner privilege
108108
clitest.SetupConfig(t,client,root)
109109
err:=inv.Run()
@@ -119,7 +119,7 @@ func TestSupportBundle(t *testing.T) {
119119
OrganizationID:user.OrganizationID,
120120
OwnerID:member.ID,
121121
}).WithAgent().Do()
122-
inv,root:=clitest.New(t,"support","bundle",r.Workspace.Name)
122+
inv,root:=clitest.New(t,"support","bundle",r.Workspace.Name,"--yes")
123123
clitest.SetupConfig(t,memberClient,root)
124124
err:=inv.Run()
125125
require.ErrorContains(t,err,"failed authorization check")
@@ -219,6 +219,9 @@ func assertBundleContents(t *testing.T, path string) {
219219
case"logs.txt":
220220
bs:=readBytesFromZip(t,f)
221221
require.NotEmpty(t,bs,"logs should not be empty")
222+
case"cli_logs.txt":
223+
bs:=readBytesFromZip(t,f)
224+
require.NotEmpty(t,bs,"CLI logs should not be empty")
222225
default:
223226
require.Failf(t,"unexpected file in bundle",f.Name)
224227
}

‎support/support.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Bundle struct {
3333
WorkspaceWorkspace`json:"workspace"`
3434
AgentAgent`json:"agent"`
3535
Logs []string`json:"logs"`
36+
CLILogs []byte`json:"cli_logs"`
3637
}
3738

3839
typeDeploymentstruct {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp