mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
feat(cli/ssh): implement wait options and deprecate no-wait (#7894)
Fixes #7768 Refs #7893
This commit is contained in:
committed by
GitHub
parent
b2324325fa
commit
94aa9be33a
@ -27,7 +27,7 @@ type AgentOptions struct {
|
|||||||
Fetch func(context.Context) (codersdk.WorkspaceAgent, error)
|
Fetch func(context.Context) (codersdk.WorkspaceAgent, error)
|
||||||
FetchInterval time.Duration
|
FetchInterval time.Duration
|
||||||
WarnInterval time.Duration
|
WarnInterval time.Duration
|
||||||
NoWait bool // If true, don't wait for the agent to be ready.
|
Wait bool // If true, wait for the agent to be ready (startup script).
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agent displays a spinning indicator that waits for a workspace agent to connect.
|
// 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 {
|
|||||||
// we do this just before starting the spinner to avoid needless
|
// we do this just before starting the spinner to avoid needless
|
||||||
// spinning.
|
// spinning.
|
||||||
if agent.Status == codersdk.WorkspaceAgentConnected &&
|
if agent.Status == codersdk.WorkspaceAgentConnected &&
|
||||||
agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && opts.NoWait {
|
agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && !opts.Wait {
|
||||||
showMessage()
|
showMessage()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
|
|||||||
// NOTE(mafredri): Once we have access to the workspace agent's
|
// NOTE(mafredri): Once we have access to the workspace agent's
|
||||||
// startup script logs, we can show them here.
|
// startup script logs, we can show them here.
|
||||||
// https://github.com/coder/coder/issues/2957
|
// https://github.com/coder/coder/issues/2957
|
||||||
if agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && !opts.NoWait {
|
if agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && opts.Wait {
|
||||||
switch agent.LifecycleState {
|
switch agent.LifecycleState {
|
||||||
case codersdk.WorkspaceAgentLifecycleReady:
|
case codersdk.WorkspaceAgentLifecycleReady:
|
||||||
return nil
|
return nil
|
||||||
@ -183,7 +183,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
|
|||||||
Prompt: "Don't panic, your workspace is booting up!",
|
Prompt: "Don't panic, your workspace is booting up!",
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if agent.Status == codersdk.WorkspaceAgentConnected && opts.NoWait {
|
if agent.Status == codersdk.WorkspaceAgentConnected && !opts.Wait {
|
||||||
m.Spin = ""
|
m.Spin = ""
|
||||||
}
|
}
|
||||||
if m.Spin != "" {
|
if m.Spin != "" {
|
||||||
@ -225,7 +225,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
|
|||||||
case codersdk.WorkspaceAgentConnected:
|
case codersdk.WorkspaceAgentConnected:
|
||||||
m.Spin = fmt.Sprintf("Waiting for %s to become ready...", DefaultStyles.Field.Render(agent.Name))
|
m.Spin = fmt.Sprintf("Waiting for %s to become ready...", DefaultStyles.Field.Render(agent.Name))
|
||||||
m.Prompt = "Don't panic, your workspace agent has connected and the workspace is getting ready!"
|
m.Prompt = "Don't panic, your workspace agent has connected and the workspace is getting ready!"
|
||||||
if opts.NoWait {
|
if !opts.Wait {
|
||||||
m.Prompt = "Your workspace is still getting ready, it may be in an incomplete state."
|
m.Prompt = "Your workspace is still getting ready, it may be in an incomplete state."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ func TestAgent_StartupTimeout(t *testing.T) {
|
|||||||
},
|
},
|
||||||
FetchInterval: time.Millisecond,
|
FetchInterval: time.Millisecond,
|
||||||
WarnInterval: time.Millisecond,
|
WarnInterval: time.Millisecond,
|
||||||
NoWait: false,
|
Wait: true,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@ -199,7 +199,7 @@ func TestAgent_StartErrorExit(t *testing.T) {
|
|||||||
},
|
},
|
||||||
FetchInterval: time.Millisecond,
|
FetchInterval: time.Millisecond,
|
||||||
WarnInterval: 60 * time.Second,
|
WarnInterval: 60 * time.Second,
|
||||||
NoWait: false,
|
Wait: true,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@ -255,7 +255,7 @@ func TestAgent_NoWait(t *testing.T) {
|
|||||||
},
|
},
|
||||||
FetchInterval: time.Millisecond,
|
FetchInterval: time.Millisecond,
|
||||||
WarnInterval: time.Second,
|
WarnInterval: time.Second,
|
||||||
NoWait: true,
|
Wait: false,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@ -325,7 +325,7 @@ func TestAgent_StartupScriptBehaviorNonBlocking(t *testing.T) {
|
|||||||
},
|
},
|
||||||
FetchInterval: time.Millisecond,
|
FetchInterval: time.Millisecond,
|
||||||
WarnInterval: time.Second,
|
WarnInterval: time.Second,
|
||||||
NoWait: false,
|
Wait: true,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
@ -92,6 +92,7 @@ func (r *RootCmd) portForward() *clibase.Cmd {
|
|||||||
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
||||||
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
|
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
|
||||||
},
|
},
|
||||||
|
Wait: false,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("await agent: %w", err)
|
return xerrors.Errorf("await agent: %w", err)
|
||||||
|
@ -45,6 +45,7 @@ func (r *RootCmd) speedtest() *clibase.Cmd {
|
|||||||
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
||||||
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
|
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
|
||||||
},
|
},
|
||||||
|
Wait: false,
|
||||||
})
|
})
|
||||||
if err != nil && !xerrors.Is(err, cliui.AgentStartError) {
|
if err != nil && !xerrors.Is(err, cliui.AgentStartError) {
|
||||||
return xerrors.Errorf("await agent: %w", err)
|
return xerrors.Errorf("await agent: %w", err)
|
||||||
|
39
cli/ssh.go
39
cli/ssh.go
@ -42,6 +42,7 @@ var (
|
|||||||
autostopNotifyCountdown = []time.Duration{30 * time.Minute}
|
autostopNotifyCountdown = []time.Duration{30 * time.Minute}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func (r *RootCmd) ssh() *clibase.Cmd {
|
func (r *RootCmd) ssh() *clibase.Cmd {
|
||||||
var (
|
var (
|
||||||
stdio bool
|
stdio bool
|
||||||
@ -49,6 +50,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
|
|||||||
forwardGPG bool
|
forwardGPG bool
|
||||||
identityAgent string
|
identityAgent string
|
||||||
wsPollInterval time.Duration
|
wsPollInterval time.Duration
|
||||||
|
waitEnum string
|
||||||
noWait bool
|
noWait bool
|
||||||
logDir string
|
logDir string
|
||||||
logToFile bool
|
logToFile bool
|
||||||
@ -105,6 +107,30 @@ func (r *RootCmd) ssh() *clibase.Cmd {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select the startup script behavior based on template configuration or flags.
|
||||||
|
var wait bool
|
||||||
|
switch waitEnum {
|
||||||
|
case "yes":
|
||||||
|
wait = true
|
||||||
|
case "no":
|
||||||
|
wait = false
|
||||||
|
case "auto":
|
||||||
|
switch workspaceAgent.StartupScriptBehavior {
|
||||||
|
case codersdk.WorkspaceAgentStartupScriptBehaviorBlocking:
|
||||||
|
wait = true
|
||||||
|
case codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking:
|
||||||
|
wait = false
|
||||||
|
default:
|
||||||
|
return xerrors.Errorf("unknown startup script behavior %q", workspaceAgent.StartupScriptBehavior)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return xerrors.Errorf("unknown wait value %q", waitEnum)
|
||||||
|
}
|
||||||
|
// The `--no-wait` flag is deprecated, but for now, check it.
|
||||||
|
if noWait {
|
||||||
|
wait = false
|
||||||
|
}
|
||||||
|
|
||||||
templateVersion, err := client.TemplateVersion(ctx, workspace.LatestBuild.TemplateVersionID)
|
templateVersion, err := client.TemplateVersion(ctx, workspace.LatestBuild.TemplateVersionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -134,7 +160,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
|
|||||||
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
||||||
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
|
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
|
||||||
},
|
},
|
||||||
NoWait: noWait,
|
Wait: wait,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if xerrors.Is(err, context.Canceled) {
|
if xerrors.Is(err, context.Canceled) {
|
||||||
@ -313,6 +339,13 @@ func (r *RootCmd) ssh() *clibase.Cmd {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
waitOption := clibase.Option{
|
||||||
|
Flag: "wait",
|
||||||
|
Env: "CODER_SSH_WAIT",
|
||||||
|
Description: "Specifies whether or not to wait for the startup script to finish executing. Auto means that the agent startup script behavior configured in the workspace template is used.",
|
||||||
|
Default: "auto",
|
||||||
|
Value: clibase.EnumOf(&waitEnum, "yes", "no", "auto"),
|
||||||
|
}
|
||||||
cmd.Options = clibase.OptionSet{
|
cmd.Options = clibase.OptionSet{
|
||||||
{
|
{
|
||||||
Flag: "stdio",
|
Flag: "stdio",
|
||||||
@ -347,11 +380,13 @@ func (r *RootCmd) ssh() *clibase.Cmd {
|
|||||||
Default: "1m",
|
Default: "1m",
|
||||||
Value: clibase.DurationOf(&wsPollInterval),
|
Value: clibase.DurationOf(&wsPollInterval),
|
||||||
},
|
},
|
||||||
|
waitOption,
|
||||||
{
|
{
|
||||||
Flag: "no-wait",
|
Flag: "no-wait",
|
||||||
Env: "CODER_SSH_NO_WAIT",
|
Env: "CODER_SSH_NO_WAIT",
|
||||||
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.",
|
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.",
|
||||||
Value: clibase.BoolOf(&noWait),
|
Value: clibase.BoolOf(&noWait),
|
||||||
|
UseInstead: []clibase.Option{waitOption},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Flag: "log-dir",
|
Flag: "log-dir",
|
||||||
|
15
cli/testdata/coder_ssh_--help.golden
vendored
15
cli/testdata/coder_ssh_--help.golden
vendored
@ -25,15 +25,18 @@ Start a shell into a workspace
|
|||||||
Enable diagnostic logging to file.
|
Enable diagnostic logging to file.
|
||||||
|
|
||||||
--no-wait bool, $CODER_SSH_NO_WAIT
|
--no-wait bool, $CODER_SSH_NO_WAIT
|
||||||
Specifies whether to wait for a workspace to become ready before
|
Enter workspace immediately after the agent has connected. This is the
|
||||||
logging in (only applicable when the startup script behavior is
|
default if the template has configured the agent startup script
|
||||||
blocking). Note that the workspace agent may still be in the process
|
behavior as non-blocking.
|
||||||
of executing the startup script and the workspace may be in an
|
DEPRECATED
|
||||||
incomplete state.
|
|
||||||
|
|
||||||
--stdio bool, $CODER_SSH_STDIO
|
--stdio bool, $CODER_SSH_STDIO
|
||||||
Specifies whether to emit SSH output over stdin/stdout.
|
Specifies whether to emit SSH output over stdin/stdout.
|
||||||
|
|
||||||
|
--wait yes|no|auto, $CODER_SSH_WAIT (default: auto)
|
||||||
|
Specifies whether or not to wait for the startup script to finish
|
||||||
|
executing. Auto means that the agent startup script behavior
|
||||||
|
configured in the workspace template is used.
|
||||||
|
|
||||||
--workspace-poll-interval duration, $CODER_WORKSPACE_POLL_INTERVAL (default: 1m)
|
--workspace-poll-interval duration, $CODER_WORKSPACE_POLL_INTERVAL (default: 1m)
|
||||||
Specifies how often to poll for workspace automated shutdown.
|
Specifies how often to poll for workspace automated shutdown.
|
||||||
|
|
||||||
|
@ -3060,6 +3060,7 @@ func (q *fakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser
|
|||||||
Architecture: arg.Architecture,
|
Architecture: arg.Architecture,
|
||||||
OperatingSystem: arg.OperatingSystem,
|
OperatingSystem: arg.OperatingSystem,
|
||||||
Directory: arg.Directory,
|
Directory: arg.Directory,
|
||||||
|
StartupScriptBehavior: arg.StartupScriptBehavior,
|
||||||
StartupScript: arg.StartupScript,
|
StartupScript: arg.StartupScript,
|
||||||
InstanceMetadata: arg.InstanceMetadata,
|
InstanceMetadata: arg.InstanceMetadata,
|
||||||
ResourceMetadata: arg.ResourceMetadata,
|
ResourceMetadata: arg.ResourceMetadata,
|
||||||
|
@ -65,7 +65,7 @@ Enable diagnostic logging to file.
|
|||||||
| Type | <code>bool</code> |
|
| Type | <code>bool</code> |
|
||||||
| Environment | <code>$CODER_SSH_NO_WAIT</code> |
|
| Environment | <code>$CODER_SSH_NO_WAIT</code> |
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
### --stdio
|
### --stdio
|
||||||
|
|
||||||
@ -76,6 +76,16 @@ Specifies whether to wait for a workspace to become ready before logging in (onl
|
|||||||
|
|
||||||
Specifies whether to emit SSH output over stdin/stdout.
|
Specifies whether to emit SSH output over stdin/stdout.
|
||||||
|
|
||||||
|
### --wait
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| ----------- | ---------------------------- | --- | ------------ |
|
||||||
|
| Type | <code>enum[yes | no | auto]</code> |
|
||||||
|
| Environment | <code>$CODER_SSH_WAIT</code> |
|
||||||
|
| Default | <code>auto</code> |
|
||||||
|
|
||||||
|
Specifies whether or not to wait for the startup script to finish executing. Auto means that the agent startup script behavior configured in the workspace template is used.
|
||||||
|
|
||||||
### --workspace-poll-interval
|
### --workspace-poll-interval
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|
Reference in New Issue
Block a user