Skip to content

Commit 08da521

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

File tree

6 files changed

+45
-16
lines changed

6 files changed

+45
-16
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

+26-2
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ var (
4242
autostopNotifyCountdown = []time.Duration{30 * time.Minute}
4343
)
4444

45+
//nolint:gocyclo
4546
func (r *RootCmd) ssh() *clibase.Cmd {
4647
var (
4748
stdio bool
4849
forwardAgent bool
4950
forwardGPG bool
5051
identityAgent string
5152
wsPollInterval time.Duration
53+
wait bool
5254
noWait bool
5355
logDir string
5456
logToFile bool
@@ -66,6 +68,10 @@ func (r *RootCmd) ssh() *clibase.Cmd {
6668
ctx, cancel := context.WithCancel(inv.Context())
6769
defer cancel()
6870

71+
if wait && noWait {
72+
return xerrors.New("cannot specify both --wait and --no-wait")
73+
}
74+
6975
logger := slog.Make() // empty logger
7076
defer func() {
7177
if retErr != nil {
@@ -105,6 +111,18 @@ func (r *RootCmd) ssh() *clibase.Cmd {
105111
return err
106112
}
107113

114+
// Select the startup script behavior based on template configuration or flags.
115+
if !wait && !noWait {
116+
switch workspaceAgent.StartupScriptBehavior {
117+
case codersdk.WorkspaceAgentStartupScriptBehaviorBlocking:
118+
wait = true
119+
case codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking:
120+
wait = false
121+
}
122+
} else if noWait {
123+
wait = false
124+
}
125+
108126
templateVersion, err := client.TemplateVersion(ctx, workspace.LatestBuild.TemplateVersionID)
109127
if err != nil {
110128
return err
@@ -134,7 +152,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
134152
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
135153
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
136154
},
137-
NoWait: noWait,
155+
Wait: wait,
138156
})
139157
if err != nil {
140158
if xerrors.Is(err, context.Canceled) {
@@ -347,10 +365,16 @@ func (r *RootCmd) ssh() *clibase.Cmd {
347365
Default: "1m",
348366
Value: clibase.DurationOf(&wsPollInterval),
349367
},
368+
{
369+
Flag: "wait",
370+
Env: "CODER_SSH_WAIT",
371+
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.",
372+
Value: clibase.BoolOf(&wait),
373+
},
350374
{
351375
Flag: "no-wait",
352376
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.",
377+
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.",
354378
Value: clibase.BoolOf(&noWait),
355379
},
356380
{

cli/testdata/coder_ssh_--help.golden

+8-5
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,18 @@ Start a shell into a workspace
2525
Enable diagnostic logging to file.
2626

2727
--no-wait bool, $CODER_SSH_NO_WAIT
28-
Specifies whether to wait for a workspace to become ready before
29-
logging in (only applicable when the startup script behavior is
30-
blocking). Note that the workspace agent may still be in the process
31-
of executing the startup script and the workspace may be in an
32-
incomplete state.
28+
Enter workspace immediately after the agent has connected. This is the
29+
default if the template has configured the agent startup script
30+
behavior as non-blocking. Can not be used together with --wait.
3331

3432
--stdio bool, $CODER_SSH_STDIO
3533
Specifies whether to emit SSH output over stdin/stdout.
3634

35+
--wait bool, $CODER_SSH_WAIT
36+
Wait for the the startup script to finish executing. This is the
37+
default if the template has configured the agent startup script
38+
behavior as blocking. Can not be used together with --no-wait.
39+
3740
--workspace-poll-interval duration, $CODER_WORKSPACE_POLL_INTERVAL (default: 1m)
3841
Specifies how often to poll for workspace automated shutdown.
3942

0 commit comments

Comments
 (0)