11package agentcontainers
22
33import (
4- "bytes"
54"context"
65"errors"
76"fmt"
8- "io"
97"net/http"
108"os"
119"path"
@@ -28,6 +26,7 @@ import (
2826"github.com/coder/coder/v2/coderd/httpapi"
2927"github.com/coder/coder/v2/codersdk"
3028"github.com/coder/coder/v2/codersdk/agentsdk"
29+ "github.com/coder/coder/v2/provisioner"
3130"github.com/coder/quartz"
3231)
3332
@@ -1113,27 +1112,6 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11131112if proc .agent .ID == uuid .Nil || maybeRecreateSubAgent {
11141113subAgentConfig .Architecture = arch
11151114
1116- // Detect workspace folder by executing `pwd` in the container.
1117- // NOTE(mafredri): This is a quick and dirty way to detect the
1118- // workspace folder inside the container. In the future we will
1119- // rely more on `devcontainer read-configuration`.
1120- var pwdBuf bytes.Buffer
1121- err = api .dccli .Exec (ctx ,dc .WorkspaceFolder ,dc .ConfigPath ,"pwd" , []string {},
1122- WithExecOutput (& pwdBuf ,io .Discard ),
1123- WithExecContainerID (container .ID ),
1124- )
1125- if err != nil {
1126- return xerrors .Errorf ("check workspace folder in container: %w" ,err )
1127- }
1128- directory := strings .TrimSpace (pwdBuf .String ())
1129- if directory == "" {
1130- logger .Warn (ctx ,"detected workspace folder is empty, using default workspace folder" ,
1131- slog .F ("default_workspace_folder" ,DevcontainerDefaultContainerWorkspaceFolder ),
1132- )
1133- directory = DevcontainerDefaultContainerWorkspaceFolder
1134- }
1135- subAgentConfig .Directory = directory
1136-
11371115displayAppsMap := map [codersdk.DisplayApp ]bool {
11381116// NOTE(DanielleMaywood):
11391117// We use the same defaults here as set in terraform-provider-coder.
@@ -1145,18 +1123,55 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11451123codersdk .DisplayAppPortForward :true ,
11461124}
11471125
1148- var appsWithPossibleDuplicates []SubAgentApp
1126+ var (
1127+ appsWithPossibleDuplicates []SubAgentApp
1128+ workspaceFolder = DevcontainerDefaultContainerWorkspaceFolder
1129+ )
1130+
1131+ if err := func ()error {
1132+ var (
1133+ config DevcontainerConfig
1134+ configOutdated bool
1135+ )
1136+
1137+ readConfig := func () (DevcontainerConfig ,error ) {
1138+ return api .dccli .ReadConfig (ctx ,dc .WorkspaceFolder ,dc .ConfigPath , []string {
1139+ fmt .Sprintf ("CODER_WORKSPACE_AGENT_NAME=%s" ,subAgentConfig .Name ),
1140+ fmt .Sprintf ("CODER_WORKSPACE_OWNER_NAME=%s" ,api .ownerName ),
1141+ fmt .Sprintf ("CODER_WORKSPACE_NAME=%s" ,api .workspaceName ),
1142+ fmt .Sprintf ("CODER_URL=%s" ,api .subAgentURL ),
1143+ })
1144+ }
1145+
1146+ if config ,err = readConfig ();err != nil {
1147+ return err
1148+ }
1149+
1150+ workspaceFolder = config .Workspace .WorkspaceFolder
1151+
1152+ // NOTE(DanielleMaywood):
1153+ // We only want to take an agent name specified in the root customization layer.
1154+ // This restricts the ability for a feature to specify the agent name. We may revisit
1155+ // this in the future, but for now we want to restrict this behavior.
1156+ if name := config .Configuration .Customizations .Coder .Name ;name != "" {
1157+ // We only want to pick this name if it is a valid name.
1158+ if provisioner .AgentNameRegex .Match ([]byte (name )) {
1159+ subAgentConfig .Name = name
1160+ configOutdated = true
1161+ }else {
1162+ logger .Warn (ctx ,"invalid name in devcontainer customization, ignoring" ,
1163+ slog .F ("name" ,name ),
1164+ slog .F ("regex" ,provisioner .AgentNameRegex .String ()),
1165+ )
1166+ }
1167+ }
1168+
1169+ if configOutdated {
1170+ if config ,err = readConfig ();err != nil {
1171+ return err
1172+ }
1173+ }
11491174
1150- if config ,err := api .dccli .ReadConfig (ctx ,dc .WorkspaceFolder ,dc .ConfigPath ,
1151- []string {
1152- fmt .Sprintf ("CODER_WORKSPACE_AGENT_NAME=%s" ,dc .Name ),
1153- fmt .Sprintf ("CODER_WORKSPACE_OWNER_NAME=%s" ,api .ownerName ),
1154- fmt .Sprintf ("CODER_WORKSPACE_NAME=%s" ,api .workspaceName ),
1155- fmt .Sprintf ("CODER_URL=%s" ,api .subAgentURL ),
1156- },
1157- );err != nil {
1158- api .logger .Error (ctx ,"unable to read devcontainer config" ,slog .Error (err ))
1159- }else {
11601175coderCustomization := config .MergedConfiguration .Customizations .Coder
11611176
11621177for _ ,customization := range coderCustomization {
@@ -1173,6 +1188,10 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
11731188
11741189appsWithPossibleDuplicates = append (appsWithPossibleDuplicates ,customization .Apps ... )
11751190}
1191+
1192+ return nil
1193+ }();err != nil {
1194+ api .logger .Error (ctx ,"unable to read devcontainer config" ,slog .Error (err ))
11761195}
11771196
11781197displayApps := make ([]codersdk.DisplayApp ,0 ,len (displayAppsMap ))
@@ -1204,6 +1223,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
12041223
12051224subAgentConfig .DisplayApps = displayApps
12061225subAgentConfig .Apps = apps
1226+ subAgentConfig .Directory = workspaceFolder
12071227}
12081228
12091229deleteSubAgent := proc .agent .ID != uuid .Nil && maybeRecreateSubAgent && ! proc .agent .EqualConfig (subAgentConfig )