Skip to content

Commit ffbdf29

Browse files
committed
feat(cli): Add wait and no-wait support to ssh
Fixes #7768 Refs #7893
1 parent a77b48a commit ffbdf29

File tree

5 files changed

+36
-11
lines changed

5 files changed

+36
-11
lines changed

cli/cliui/agent.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type AgentOptions struct {
2727
Fetch func(context.Context) (codersdk.WorkspaceAgent, error)
2828
FetchInterval time.Duration
2929
WarnInterval time.Duration
30-
NoWait bool // If true, don't wait for the agent to be ready.
30+
Wait bool // If true, wait for the agent to be ready (startup script).
3131
}
3232

3333
// Agent displays a spinning indicator that waits for a workspace agent to connect.
@@ -96,7 +96,7 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
9696
// we do this just before starting the spinner to avoid needless
9797
// spinning.
9898
if agent.Status == codersdk.WorkspaceAgentConnected &&
99-
agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && opts.NoWait {
99+
agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && !opts.Wait {
100100
showMessage()
101101
return nil
102102
}
@@ -140,7 +140,7 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
140140
// NOTE(mafredri): Once we have access to the workspace agent's
141141
// startup script logs, we can show them here.
142142
// https://github.com/coder/coder/issues/2957
143-
if agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && !opts.NoWait {
143+
if agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && opts.Wait {
144144
switch agent.LifecycleState {
145145
case codersdk.WorkspaceAgentLifecycleReady:
146146
return nil
@@ -183,7 +183,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
183183
Prompt: "Don't panic, your workspace is booting up!",
184184
}
185185
defer func() {
186-
if agent.Status == codersdk.WorkspaceAgentConnected && opts.NoWait {
186+
if agent.Status == codersdk.WorkspaceAgentConnected && !opts.Wait {
187187
m.Spin = ""
188188
}
189189
if m.Spin != "" {
@@ -225,7 +225,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
225225
case codersdk.WorkspaceAgentConnected:
226226
m.Spin = fmt.Sprintf("Waiting for %s to become ready...", DefaultStyles.Field.Render(agent.Name))
227227
m.Prompt = "Don't panic, your workspace agent has connected and the workspace is getting ready!"
228-
if opts.NoWait {
228+
if !opts.Wait {
229229
m.Prompt = "Your workspace is still getting ready, it may be in an incomplete state."
230230
}
231231

cli/cliui/agent_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func TestAgent_StartupTimeout(t *testing.T) {
140140
},
141141
FetchInterval: time.Millisecond,
142142
WarnInterval: time.Millisecond,
143-
NoWait: false,
143+
Wait: true,
144144
})
145145
return err
146146
},
@@ -199,7 +199,7 @@ func TestAgent_StartErrorExit(t *testing.T) {
199199
},
200200
FetchInterval: time.Millisecond,
201201
WarnInterval: 60 * time.Second,
202-
NoWait: false,
202+
Wait: true,
203203
})
204204
return err
205205
},
@@ -255,7 +255,7 @@ func TestAgent_NoWait(t *testing.T) {
255255
},
256256
FetchInterval: time.Millisecond,
257257
WarnInterval: time.Second,
258-
NoWait: true,
258+
Wait: false,
259259
})
260260
return err
261261
},
@@ -325,7 +325,7 @@ func TestAgent_StartupScriptBehaviorNonBlocking(t *testing.T) {
325325
},
326326
FetchInterval: time.Millisecond,
327327
WarnInterval: time.Second,
328-
NoWait: false,
328+
Wait: true,
329329
})
330330
return err
331331
},

cli/portforward.go

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ func (r *RootCmd) portForward() *clibase.Cmd {
9292
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
9393
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
9494
},
95+
Wait: false,
9596
})
9697
if err != nil {
9798
return xerrors.Errorf("await agent: %w", err)

cli/speedtest.go

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func (r *RootCmd) speedtest() *clibase.Cmd {
4545
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
4646
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
4747
},
48+
Wait: false,
4849
})
4950
if err != nil && !xerrors.Is(err, cliui.AgentStartError) {
5051
return xerrors.Errorf("await agent: %w", err)

cli/ssh.go

+25-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
4949
forwardGPG bool
5050
identityAgent string
5151
wsPollInterval time.Duration
52+
wait bool
5253
noWait bool
5354
logDir string
5455
logToFile bool
@@ -66,6 +67,10 @@ func (r *RootCmd) ssh() *clibase.Cmd {
6667
ctx, cancel := context.WithCancel(inv.Context())
6768
defer cancel()
6869

70+
if wait && noWait {
71+
return xerrors.New("cannot specify both --wait and --no-wait")
72+
}
73+
6974
logger := slog.Make() // empty logger
7075
defer func() {
7176
if retErr != nil {
@@ -105,6 +110,18 @@ func (r *RootCmd) ssh() *clibase.Cmd {
105110
return err
106111
}
107112

113+
// Select the startup script behavior based on template configuration or flags.
114+
if !wait && !noWait {
115+
switch workspaceAgent.StartupScriptBehavior {
116+
case codersdk.WorkspaceAgentStartupScriptBehaviorBlocking:
117+
wait = true
118+
case codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking:
119+
wait = false
120+
}
121+
} else if noWait {
122+
wait = false
123+
}
124+
108125
templateVersion, err := client.TemplateVersion(ctx, workspace.LatestBuild.TemplateVersionID)
109126
if err != nil {
110127
return err
@@ -134,7 +151,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
134151
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
135152
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
136153
},
137-
NoWait: noWait,
154+
Wait: wait,
138155
})
139156
if err != nil {
140157
if xerrors.Is(err, context.Canceled) {
@@ -347,10 +364,16 @@ func (r *RootCmd) ssh() *clibase.Cmd {
347364
Default: "1m",
348365
Value: clibase.DurationOf(&wsPollInterval),
349366
},
367+
{
368+
Flag: "wait",
369+
Env: "CODER_SSH_WAIT",
370+
Description: "Wait for the the startup script to finish executing. This is the default if the template has configured the agent startup script behavior as blocking. Can not be used together with --no-wait.",
371+
Value: clibase.BoolOf(&wait),
372+
},
350373
{
351374
Flag: "no-wait",
352375
Env: "CODER_SSH_NO_WAIT",
353-
Description: "Specifies whether to wait for a workspace to become ready before logging in (only applicable when the startup script behavior is blocking). Note that the workspace agent may still be in the process of executing the startup script and the workspace may be in an incomplete state.",
376+
Description: "Enter workspace immediately after the agent has connected. This is the default if the template has configured the agent startup script behavior as non-blocking. Can not be used together with --wait.",
354377
Value: clibase.BoolOf(&noWait),
355378
},
356379
{

0 commit comments

Comments
 (0)