@@ -754,7 +754,8 @@ func findWorkspaceAndAgentByHostname(
754
754
hostname = strings .TrimSuffix (hostname ,qualifiedSuffix )
755
755
}
756
756
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
758
759
}
759
760
760
761
// watchAndClose ensures closer is called if the context is canceled or
@@ -827,9 +828,10 @@ startWatchLoop:
827
828
}
828
829
829
830
// 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.
831
833
// 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
833
835
var (
834
836
workspace codersdk.Workspace
835
837
// The input will be `owner/name.agent`
@@ -840,27 +842,27 @@ func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *
840
842
841
843
workspace ,err = namedWorkspace (ctx ,client ,workspaceParts [0 ])
842
844
if err != nil {
843
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},err
845
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil , err
844
846
}
845
847
846
848
if workspace .LatestBuild .Transition != codersdk .WorkspaceTransitionStart {
847
849
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" )
849
851
}
850
852
// Autostart the workspace for the user.
851
853
// For some failure modes, return a better message.
852
854
if workspace .LatestBuild .Transition == codersdk .WorkspaceTransitionDelete {
853
855
// 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 )
855
857
}
856
858
if workspace .LatestBuild .Job .Status == codersdk .ProvisionerJobFailed {
857
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},
859
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil ,
858
860
xerrors .Errorf ("workspace %q is in failed state, unable to autostart the workspace" ,workspace .Name )
859
861
}
860
862
// The workspace needs to be stopped before we can start it.
861
863
// It cannot be in any pending or failed state.
862
864
if workspace .LatestBuild .Status != codersdk .WorkspaceStatusStopped {
863
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},
865
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil ,
864
866
xerrors .Errorf ("workspace must be started; was unable to autostart as the last build job is %q, expected %q" ,
865
867
workspace .LatestBuild .Status ,
866
868
codersdk .WorkspaceStatusStopped ,
@@ -881,48 +883,48 @@ func getWorkspaceAndAgent(ctx context.Context, inv *serpent.Invocation, client *
881
883
case http .StatusForbidden :
882
884
_ ,err = startWorkspace (inv ,client ,workspace ,workspaceParameterFlags {},buildFlags {},WorkspaceUpdate )
883
885
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 )
885
887
}
886
888
_ ,_ = 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." )
887
889
}
888
890
}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 )
890
892
}
891
893
892
894
// Refresh workspace state so that `outdated`, `build`,`template_*` fields are up-to-date.
893
895
workspace ,err = namedWorkspace (ctx ,client ,workspaceParts [0 ])
894
896
if err != nil {
895
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},err
897
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil , err
896
898
}
897
899
}
898
900
if workspace .LatestBuild .Job .CompletedAt == nil {
899
901
err := cliui .WorkspaceBuild (ctx ,inv .Stderr ,client ,workspace .LatestBuild .ID )
900
902
if err != nil {
901
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},err
903
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil , err
902
904
}
903
905
// Fetch up-to-date build information after completion.
904
906
workspace .LatestBuild ,err = client .WorkspaceBuild (ctx ,workspace .LatestBuild .ID )
905
907
if err != nil {
906
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},err
908
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil , err
907
909
}
908
910
}
909
911
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 )
911
913
}
912
914
913
915
var agentName string
914
916
if len (workspaceParts )>= 2 {
915
917
agentName = workspaceParts [1 ]
916
918
}
917
- workspaceAgent ,err := getWorkspaceAgent (workspace ,agentName )
919
+ workspaceAgent ,otherWorkspaceAgents , err := getWorkspaceAgent (workspace ,agentName )
918
920
if err != nil {
919
- return codersdk.Workspace {}, codersdk.WorkspaceAgent {},err
921
+ return codersdk.Workspace {}, codersdk.WorkspaceAgent {},nil , err
920
922
}
921
923
922
- return workspace ,workspaceAgent ,nil
924
+ return workspace ,workspaceAgent ,otherWorkspaceAgents , nil
923
925
}
924
926
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 ) {
926
928
resources := workspace .LatestBuild .Resources
927
929
928
930
var (
@@ -936,22 +938,23 @@ func getWorkspaceAgent(workspace codersdk.Workspace, agentName string) (workspac
936
938
}
937
939
}
938
940
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 )
940
942
}
941
943
slices .Sort (availableNames )
942
944
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 {
945
947
continue
946
948
}
947
- return otherAgent ,nil
949
+ otherAgents := slices .Delete (agents ,i ,i + 1 )
950
+ return agent ,otherAgents ,nil
948
951
}
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 )
950
953
}
951
954
if len (agents )== 1 {
952
- return agents [0 ],nil
955
+ return agents [0 ],nil , nil
953
956
}
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 )
955
958
}
956
959
957
960
// Attempt to poll workspace autostop. We write a per-workspace lockfile to