Skip to content

Commit 627957c

Browse files
committed
cli: return other agents from getWorkspaceAndAgent
1 parent 4ae42b5 commit 627957c

File tree

8 files changed

+45
-39
lines changed

8 files changed

+45
-39
lines changed

cli/exp_rpty.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func handleRPTY(inv *serpent.Invocation, client *codersdk.Client, args handleRPT
9797
reconnectID = uuid.New()
9898
}
9999

100-
ws, agt, err := getWorkspaceAndAgent(ctx, inv, client, true, args.NamedWorkspace)
100+
ws, agt, _, err := getWorkspaceAndAgent(ctx, inv, client, true, args.NamedWorkspace)
101101
if err != nil {
102102
return err
103103
}

cli/open.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (r *RootCmd) openVSCode() *serpent.Command {
7171
// need to wait for the agent to start.
7272
workspaceQuery := inv.Args[0]
7373
autostart := true
74-
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, autostart, workspaceQuery)
74+
workspace, workspaceAgent, otherWorkspaceAgents, err := getWorkspaceAndAgent(ctx, inv, client, autostart, workspaceQuery)
7575
if err != nil {
7676
return xerrors.Errorf("get workspace and agent: %w", err)
7777
}
@@ -288,7 +288,7 @@ func (r *RootCmd) openApp() *serpent.Command {
288288
}
289289

290290
workspaceName := inv.Args[0]
291-
ws, agt, err := getWorkspaceAndAgent(ctx, inv, client, false, workspaceName)
291+
ws, agt, _, err := getWorkspaceAndAgent(ctx, inv, client, false, workspaceName)
292292
if err != nil {
293293
var sdkErr *codersdk.Error
294294
if errors.As(err, &sdkErr) && sdkErr.StatusCode() == http.StatusNotFound {
@@ -469,7 +469,7 @@ func waitForAgentCond(ctx context.Context, client *codersdk.Client, workspace co
469469
}
470470

471471
for workspace = range wc {
472-
workspaceAgent, err = getWorkspaceAgent(workspace, workspaceAgent.Name)
472+
workspaceAgent, _, err = getWorkspaceAgent(workspace, workspaceAgent.Name)
473473
if err != nil {
474474
return workspace, workspaceAgent, xerrors.Errorf("get workspace agent: %w", err)
475475
}

cli/ping.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (r *RootCmd) ping() *serpent.Command {
110110
defer notifyCancel()
111111

112112
workspaceName := inv.Args[0]
113-
_, workspaceAgent, err := getWorkspaceAndAgent(
113+
_, workspaceAgent, _, err := getWorkspaceAndAgent(
114114
ctx, inv, client,
115115
false, // Do not autostart for a ping.
116116
workspaceName,

cli/portforward.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func (r *RootCmd) portForward() *serpent.Command {
8484
return xerrors.New("no port-forwards requested")
8585
}
8686

87-
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, inv.Args[0])
87+
workspace, workspaceAgent, _, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, inv.Args[0])
8888
if err != nil {
8989
return err
9090
}

cli/speedtest.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (r *RootCmd) speedtest() *serpent.Command {
8383
return xerrors.Errorf("--direct (-d) is incompatible with --%s", varDisableDirect)
8484
}
8585

86-
_, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, false, inv.Args[0])
86+
_, workspaceAgent, _, err := getWorkspaceAndAgent(ctx, inv, client, false, inv.Args[0])
8787
if err != nil {
8888
return err
8989
}

cli/ssh.go

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,8 @@ func findWorkspaceAndAgentByHostname(
754754
hostname = strings.TrimSuffix(hostname, qualifiedSuffix)
755755
}
756756
hostname = normalizeWorkspaceInput(hostname)
757-
return getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, hostname)
757+
ws, agent, _, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, hostname)
758+
return ws, agent, err
758759
}
759760

760761
// watchAndClose ensures closer is called if the context is canceled or
@@ -827,9 +828,10 @@ startWatchLoop:
827828
}
828829

829830
// getWorkspaceAgent returns the workspace and agent selected using either the
830-
// `<workspace>[.<agent>]` syntax via `in`.
831+
// `<workspace>[.<agent>]` syntax via `in`. It will also return any other agents
832+
// in the workspace as a slice for use in child->parent lookups.
831833
// If autoStart is true, the workspace will be started if it is not already running.
832-
func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *codersdk.Client, autostart bool, input string) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive
834+
func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *codersdk.Client, autostart bool, input string) (codersdk.Workspace, codersdk.WorkspaceAgent, []codersdk.WorkspaceAgent, error) { //nolint:revive
833835
var (
834836
workspace codersdk.Workspace
835837
// The input will be `owner/name.agent`
@@ -840,27 +842,27 @@ func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *
840842

841843
workspace, err = namedWorkspace(ctx, client, workspaceParts[0])
842844
if err != nil {
843-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
845+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, err
844846
}
845847

846848
if workspace.LatestBuild.Transition != codersdk.WorkspaceTransitionStart {
847849
if !autostart {
848-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.New("workspace must be started")
850+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, xerrors.New("workspace must be started")
849851
}
850852
// Autostart the workspace for the user.
851853
// For some failure modes, return a better message.
852854
if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionDelete {
853855
// Any sort of deleting status, we should reject with a nicer error.
854-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace %q is deleted", workspace.Name)
856+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("workspace %q is deleted", workspace.Name)
855857
}
856858
if workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobFailed {
857-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{},
859+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil,
858860
xerrors.Errorf("workspace %q is in failed state, unable to autostart the workspace", workspace.Name)
859861
}
860862
// The workspace needs to be stopped before we can start it.
861863
// It cannot be in any pending or failed state.
862864
if workspace.LatestBuild.Status != codersdk.WorkspaceStatusStopped {
863-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{},
865+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil,
864866
xerrors.Errorf("workspace must be started; was unable to autostart as the last build job is %q, expected %q",
865867
workspace.LatestBuild.Status,
866868
codersdk.WorkspaceStatusStopped,
@@ -881,48 +883,48 @@ func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *
881883
case http.StatusForbidden:
882884
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, buildFlags{}, WorkspaceUpdate)
883885
if err != nil {
884-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with active template version: %w", err)
886+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("start workspace with active template version: %w", err)
885887
}
886888
_, _ = fmt.Fprintln(inv.Stdout, "Unable to start the workspace with template version from last build. Your workspace has been updated to the current active template version.")
887889
}
888890
} else if err != nil {
889-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with current template version: %w", err)
891+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("start workspace with current template version: %w", err)
890892
}
891893

892894
// Refresh workspace state so that `outdated`, `build`,`template_*` fields are up-to-date.
893895
workspace, err = namedWorkspace(ctx, client, workspaceParts[0])
894896
if err != nil {
895-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
897+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, err
896898
}
897899
}
898900
if workspace.LatestBuild.Job.CompletedAt == nil {
899901
err := cliui.WorkspaceBuild(ctx, inv.Stderr, client, workspace.LatestBuild.ID)
900902
if err != nil {
901-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
903+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, err
902904
}
903905
// Fetch up-to-date build information after completion.
904906
workspace.LatestBuild, err = client.WorkspaceBuild(ctx, workspace.LatestBuild.ID)
905907
if err != nil {
906-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
908+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, err
907909
}
908910
}
909911
if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionDelete {
910-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace %q is being deleted", workspace.Name)
912+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("workspace %q is being deleted", workspace.Name)
911913
}
912914

913915
var agentName string
914916
if len(workspaceParts) >= 2 {
915917
agentName = workspaceParts[1]
916918
}
917-
workspaceAgent, err := getWorkspaceAgent(workspace, agentName)
919+
workspaceAgent, otherWorkspaceAgents, err := getWorkspaceAgent(workspace, agentName)
918920
if err != nil {
919-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
921+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, nil, err
920922
}
921923

922-
return workspace, workspaceAgent, nil
924+
return workspace, workspaceAgent, otherWorkspaceAgents, nil
923925
}
924926

925-
func getWorkspaceAgent(workspace codersdk.Workspace, agentName string) (workspaceAgent codersdk.WorkspaceAgent, err error) {
927+
func getWorkspaceAgent(workspace codersdk.Workspace, agentName string) (workspaceAgent codersdk.WorkspaceAgent, otherAgents []codersdk.WorkspaceAgent, err error) {
926928
resources := workspace.LatestBuild.Resources
927929

928930
var (
@@ -936,22 +938,23 @@ func getWorkspaceAgent(workspace codersdk.Workspace, agentName string) (workspac
936938
}
937939
}
938940
if len(agents) == 0 {
939-
return codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace %q has no agents", workspace.Name)
941+
return codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("workspace %q has no agents", workspace.Name)
940942
}
941943
slices.Sort(availableNames)
942944
if agentName != "" {
943-
for _, otherAgent := range agents {
944-
if otherAgent.Name != agentName {
945+
for i, agent := range agents {
946+
if agent.Name != agentName || agent.ID.String() == agentName {
945947
continue
946948
}
947-
return otherAgent, nil
949+
otherAgents := slices.Delete(agents, i, i+1)
950+
return agent, otherAgents, nil
948951
}
949-
return codersdk.WorkspaceAgent{}, xerrors.Errorf("agent not found by name %q, available agents: %v", agentName, availableNames)
952+
return codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("agent not found by name %q, available agents: %v", agentName, availableNames)
950953
}
951954
if len(agents) == 1 {
952-
return agents[0], nil
955+
return agents[0], nil, nil
953956
}
954-
return codersdk.WorkspaceAgent{}, xerrors.Errorf("multiple agents found, please specify the agent name, available agents: %v", availableNames)
957+
return codersdk.WorkspaceAgent{}, nil, xerrors.Errorf("multiple agents found, please specify the agent name, available agents: %v", availableNames)
955958
}
956959

957960
// Attempt to poll workspace autostop. We write a per-workspace lockfile to

cli/ssh_internal_test.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ func Test_getWorkspaceAgent(t *testing.T) {
376376
agent := createAgent("main")
377377
workspace := createWorkspaceWithAgents([]codersdk.WorkspaceAgent{agent})
378378

379-
result, err := getWorkspaceAgent(workspace, "")
379+
result, _, err := getWorkspaceAgent(workspace, "")
380380
require.NoError(t, err)
381381
assert.Equal(t, agent.ID, result.ID)
382382
assert.Equal(t, "main", result.Name)
@@ -388,7 +388,7 @@ func Test_getWorkspaceAgent(t *testing.T) {
388388
agent2 := createAgent("main2")
389389
workspace := createWorkspaceWithAgents([]codersdk.WorkspaceAgent{agent1, agent2})
390390

391-
_, err := getWorkspaceAgent(workspace, "")
391+
_, _, err := getWorkspaceAgent(workspace, "")
392392
require.Error(t, err)
393393
assert.Contains(t, err.Error(), "multiple agents found")
394394
assert.Contains(t, err.Error(), "available agents: [main1 main2]")
@@ -400,10 +400,13 @@ func Test_getWorkspaceAgent(t *testing.T) {
400400
agent2 := createAgent("main2")
401401
workspace := createWorkspaceWithAgents([]codersdk.WorkspaceAgent{agent1, agent2})
402402

403-
result, err := getWorkspaceAgent(workspace, "main1")
403+
result, other, err := getWorkspaceAgent(workspace, "main1")
404404
require.NoError(t, err)
405405
assert.Equal(t, agent1.ID, result.ID)
406406
assert.Equal(t, "main1", result.Name)
407+
assert.Len(t, other, 1)
408+
assert.Equal(t, agent2.ID, other[0].ID)
409+
assert.Equal(t, "main2", other[0].Name)
407410
})
408411

409412
t.Run("AgentNameSpecified_NotFound", func(t *testing.T) {
@@ -412,7 +415,7 @@ func Test_getWorkspaceAgent(t *testing.T) {
412415
agent2 := createAgent("main2")
413416
workspace := createWorkspaceWithAgents([]codersdk.WorkspaceAgent{agent1, agent2})
414417

415-
_, err := getWorkspaceAgent(workspace, "nonexistent")
418+
_, _, err := getWorkspaceAgent(workspace, "nonexistent")
416419
require.Error(t, err)
417420
assert.Contains(t, err.Error(), `agent not found by name "nonexistent"`)
418421
assert.Contains(t, err.Error(), "available agents: [main1 main2]")
@@ -422,7 +425,7 @@ func Test_getWorkspaceAgent(t *testing.T) {
422425
t.Parallel()
423426
workspace := createWorkspaceWithAgents([]codersdk.WorkspaceAgent{})
424427

425-
_, err := getWorkspaceAgent(workspace, "")
428+
_, _, err := getWorkspaceAgent(workspace, "")
426429
require.Error(t, err)
427430
assert.Contains(t, err.Error(), `workspace "test-workspace" has no agents`)
428431
})
@@ -435,7 +438,7 @@ func Test_getWorkspaceAgent(t *testing.T) {
435438
agent3 := createAgent("krypton")
436439
workspace := createWorkspaceWithAgents([]codersdk.WorkspaceAgent{agent2, agent1, agent3})
437440

438-
_, err := getWorkspaceAgent(workspace, "nonexistent")
441+
_, _, err := getWorkspaceAgent(workspace, "nonexistent")
439442
require.Error(t, err)
440443
// Available agents should be sorted alphabetically.
441444
assert.Contains(t, err.Error(), "available agents: [clark krypton zod]")

cli/vscodessh.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
102102
// will call this command after the workspace is started.
103103
autostart := false
104104

105-
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, autostart, fmt.Sprintf("%s/%s", owner, name))
105+
workspace, workspaceAgent, _, err := getWorkspaceAndAgent(ctx, inv, client, autostart, fmt.Sprintf("%s/%s", owner, name))
106106
if err != nil {
107107
return xerrors.Errorf("find workspace and agent: %w", err)
108108
}

0 commit comments

Comments
 (0)