1
1
package agentcontainers
2
2
3
3
import (
4
- "bytes"
5
4
"context"
6
5
"errors"
7
6
"fmt"
8
- "io"
9
7
"net/http"
10
8
"os"
11
9
"path"
@@ -28,6 +26,7 @@ import (
28
26
"github.com/coder/coder/v2/coderd/httpapi"
29
27
"github.com/coder/coder/v2/codersdk"
30
28
"github.com/coder/coder/v2/codersdk/agentsdk"
29
+ "github.com/coder/coder/v2/provisioner"
31
30
"github.com/coder/quartz"
32
31
)
33
32
@@ -1113,27 +1112,6 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1113
1112
if proc .agent .ID == uuid .Nil || maybeRecreateSubAgent {
1114
1113
subAgentConfig .Architecture = arch
1115
1114
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
-
1137
1115
displayAppsMap := map [codersdk.DisplayApp ]bool {
1138
1116
// NOTE(DanielleMaywood):
1139
1117
// 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
1145
1123
codersdk .DisplayAppPortForward :true ,
1146
1124
}
1147
1125
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
+ }
1149
1174
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 {
1160
1175
coderCustomization := config .MergedConfiguration .Customizations .Coder
1161
1176
1162
1177
for _ ,customization := range coderCustomization {
@@ -1173,6 +1188,10 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1173
1188
1174
1189
appsWithPossibleDuplicates = append (appsWithPossibleDuplicates ,customization .Apps ... )
1175
1190
}
1191
+
1192
+ return nil
1193
+ }();err != nil {
1194
+ api .logger .Error (ctx ,"unable to read devcontainer config" ,slog .Error (err ))
1176
1195
}
1177
1196
1178
1197
displayApps := make ([]codersdk.DisplayApp ,0 ,len (displayAppsMap ))
@@ -1204,6 +1223,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1204
1223
1205
1224
subAgentConfig .DisplayApps = displayApps
1206
1225
subAgentConfig .Apps = apps
1226
+ subAgentConfig .Directory = workspaceFolder
1207
1227
}
1208
1228
1209
1229
deleteSubAgent := proc .agent .ID != uuid .Nil && maybeRecreateSubAgent && ! proc .agent .EqualConfig (subAgentConfig )