@@ -671,9 +671,9 @@ func (api *API) getContainers() (codersdk.WorkspaceAgentListContainersResponse,
671671if len (api .knownDevcontainers )> 0 {
672672devcontainers = make ([]codersdk.WorkspaceAgentDevcontainer ,0 ,len (api .knownDevcontainers ))
673673for _ ,dc := range api .knownDevcontainers {
674- // Include the agent if it'sbeen created (we're iterating over
674+ // Include the agent if it'srunning (we're iterating over
675675// copies, so mutating is fine).
676- if proc := api .injectedSubAgentProcs [dc .WorkspaceFolder ];proc .agent .ID != uuid .Nil && dc . Container != nil && proc . containerID == dc . Container . ID {
676+ if proc := api .injectedSubAgentProcs [dc .WorkspaceFolder ];proc .agent .ID != uuid .Nil {
677677dc .Agent = & codersdk.WorkspaceAgentDevcontainerAgent {
678678ID :proc .agent .ID ,
679679Name :proc .agent .Name ,
@@ -977,7 +977,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
977977)
978978
979979// Check if subagent already exists for this devcontainer.
980- recreateSubAgent := false
980+ maybeRecreateSubAgent := false
981981proc ,injected := api .injectedSubAgentProcs [dc .WorkspaceFolder ]
982982if injected {
983983if proc .containerID == container .ID && proc .ctx .Err ()== nil {
@@ -992,12 +992,15 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
992992logger .Debug (ctx ,"container ID changed, injecting subagent into new container" ,
993993slog .F ("old_container_id" ,proc .containerID ),
994994)
995- recreateSubAgent = true
995+ maybeRecreateSubAgent = proc . agent . ID != uuid . Nil
996996}
997997
998998// Container ID changed or the subagent process is not running,
999999// stop the existing subagent context to replace it.
10001000proc .stop ()
1001+ }else {
1002+ // Set SubAgent defaults.
1003+ proc .agent .OperatingSystem = "linux" // Assuming Linux for devcontainers.
10011004}
10021005
10031006// Prepare the subAgentProcess to be used when running the subagent.
@@ -1090,36 +1093,29 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
10901093// logger.Warn(ctx, "set CAP_NET_ADMIN on agent binary failed", slog.Error(err))
10911094// }
10921095
1093- // Detect workspace folder by executing `pwd` in the container.
1094- // NOTE(mafredri): This is a quick and dirty way to detect the
1095- // workspace folder inside the container. In the future we will
1096- // rely more on `devcontainer read-configuration`.
1097- var pwdBuf bytes.Buffer
1098- err = api .dccli .Exec (ctx ,dc .WorkspaceFolder ,dc .ConfigPath ,"pwd" , []string {},
1099- WithExecOutput (& pwdBuf ,io .Discard ),
1100- WithExecContainerID (container .ID ),
1101- )
1102- if err != nil {
1103- return xerrors .Errorf ("check workspace folder in container: %w" ,err )
1104- }
1105- directory := strings .TrimSpace (pwdBuf .String ())
1106- if directory == "" {
1107- logger .Warn (ctx ,"detected workspace folder is empty, using default workspace folder" ,
1108- slog .F ("default_workspace_folder" ,DevcontainerDefaultContainerWorkspaceFolder ),
1096+ subAgentConfig := proc .agent .CloneConfig (dc )
1097+ if proc .agent .ID == uuid .Nil || maybeRecreateSubAgent {
1098+ // Detect workspace folder by executing `pwd` in the container.
1099+ // NOTE(mafredri): This is a quick and dirty way to detect the
1100+ // workspace folder inside the container. In the future we will
1101+ // rely more on `devcontainer read-configuration`.
1102+ var pwdBuf bytes.Buffer
1103+ err = api .dccli .Exec (ctx ,dc .WorkspaceFolder ,dc .ConfigPath ,"pwd" , []string {},
1104+ WithExecOutput (& pwdBuf ,io .Discard ),
1105+ WithExecContainerID (container .ID ),
11091106)
1110- directory = DevcontainerDefaultContainerWorkspaceFolder
1111- }
1112-
1113- if proc .agent .ID != uuid .Nil && recreateSubAgent {
1114- logger .Debug (ctx ,"deleting existing subagent for recreation" ,slog .F ("agent_id" ,proc .agent .ID ))
1115- client := * api .subAgentClient .Load ()
1116- err = client .Delete (ctx ,proc .agent .ID )
11171107if err != nil {
1118- return xerrors .Errorf ("delete existing subagent failed : %w" ,err )
1108+ return xerrors .Errorf ("check workspace folder in container : %w" ,err )
11191109}
1120- proc .agent = SubAgent {}
1121- }
1122- if proc .agent .ID == uuid .Nil {
1110+ directory := strings .TrimSpace (pwdBuf .String ())
1111+ if directory == "" {
1112+ logger .Warn (ctx ,"detected workspace folder is empty, using default workspace folder" ,
1113+ slog .F ("default_workspace_folder" ,DevcontainerDefaultContainerWorkspaceFolder ),
1114+ )
1115+ directory = DevcontainerDefaultContainerWorkspaceFolder
1116+ }
1117+ subAgentConfig .Directory = directory
1118+
11231119displayAppsMap := map [codersdk.DisplayApp ]bool {
11241120// NOTE(DanielleMaywood):
11251121// We use the same defaults here as set in terraform-provider-coder.
@@ -1138,6 +1134,13 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11381134
11391135for _ ,customization := range coderCustomization {
11401136for app ,enabled := range customization .DisplayApps {
1137+ if _ ,ok := displayAppsMap [app ];! ok {
1138+ logger .Warn (ctx ,"unknown display app in devcontainer customization, ignoring" ,
1139+ slog .F ("app" ,app ),
1140+ slog .F ("enabled" ,enabled ),
1141+ )
1142+ continue
1143+ }
11411144displayAppsMap [app ]= enabled
11421145}
11431146}
@@ -1149,26 +1152,41 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11491152displayApps = append (displayApps ,app )
11501153}
11511154}
1155+ slices .Sort (displayApps )
11521156
1157+ subAgentConfig .DisplayApps = displayApps
1158+ }
1159+
1160+ deleteSubAgent := proc .agent .ID != uuid .Nil && maybeRecreateSubAgent && ! proc .agent .EqualConfig (subAgentConfig )
1161+ if deleteSubAgent {
1162+ logger .Debug (ctx ,"deleting existing subagent for recreation" ,slog .F ("agent_id" ,proc .agent .ID ))
1163+ client := * api .subAgentClient .Load ()
1164+ err = client .Delete (ctx ,proc .agent .ID )
1165+ if err != nil {
1166+ return xerrors .Errorf ("delete existing subagent failed: %w" ,err )
1167+ }
1168+ proc .agent = SubAgent {}// Clear agent to signal that we need to create a new one.
1169+ }
1170+
1171+ if proc .agent .ID == uuid .Nil {
11531172logger .Debug (ctx ,"creating new subagent" ,
1154- slog .F ("directory" ,directory ),
1155- slog .F ("display_apps" ,displayApps ),
1173+ slog .F ("directory" ,subAgentConfig . Directory ),
1174+ slog .F ("display_apps" ,subAgentConfig . DisplayApps ),
11561175)
11571176
11581177// Create new subagent record in the database to receive the auth token.
11591178client := * api .subAgentClient .Load ()
1160- proc .agent ,err = client .Create (ctx ,SubAgent {
1161- Name :dc .Name ,
1162- Directory :directory ,
1163- OperatingSystem :"linux" ,// Assuming Linux for devcontainers.
1164- Architecture :arch ,
1165- DisplayApps :displayApps ,
1166- })
1179+ newSubAgent ,err := client .Create (ctx ,subAgentConfig )
11671180if err != nil {
11681181return xerrors .Errorf ("create subagent failed: %w" ,err )
11691182}
1183+ proc .agent = newSubAgent
11701184
11711185logger .Info (ctx ,"created new subagent" ,slog .F ("agent_id" ,proc .agent .ID ))
1186+ }else {
1187+ logger .Debug (ctx ,"subagent already exists, skipping recreation" ,
1188+ slog .F ("agent_id" ,proc .agent .ID ),
1189+ )
11721190}
11731191
11741192api .mu .Lock ()// Re-lock to update the agent.