@@ -28,6 +28,7 @@ import (
28
28
"github.com/coder/coder/v2/coderd/httpapi"
29
29
"github.com/coder/coder/v2/codersdk"
30
30
"github.com/coder/coder/v2/codersdk/agentsdk"
31
+ "github.com/coder/coder/v2/provisioner"
31
32
"github.com/coder/quartz"
32
33
)
33
34
@@ -64,6 +65,9 @@ type API struct {
64
65
subAgentURL string
65
66
subAgentEnv []string
66
67
68
+ ownerName string
69
+ workspaceName string
70
+
67
71
mu sync.RWMutex
68
72
closed bool
69
73
containers codersdk.WorkspaceAgentListContainersResponse // Output from the last list operation.
@@ -153,6 +157,15 @@ func WithSubAgentEnv(env ...string) Option {
153
157
}
154
158
}
155
159
160
+ // WithManifestInfo sets the owner name, and workspace name
161
+ // for the sub-agent.
162
+ func WithManifestInfo (owner , workspace string ) Option {
163
+ return func (api * API ) {
164
+ api .ownerName = owner
165
+ api .workspaceName = workspace
166
+ }
167
+ }
168
+
156
169
// WithDevcontainers sets the known devcontainers for the API. This
157
170
// allows the API to be aware of devcontainers defined in the workspace
158
171
// agent manifest.
@@ -1051,6 +1064,10 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1051
1064
)
1052
1065
return nil
1053
1066
}
1067
+ if proc .agent .ID == uuid .Nil {
1068
+ proc .agent .Architecture = arch
1069
+ }
1070
+
1054
1071
agentBinaryPath , err := os .Executable ()
1055
1072
if err != nil {
1056
1073
return xerrors .Errorf ("get agent binary path: %w" , err )
@@ -1095,6 +1112,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1095
1112
1096
1113
subAgentConfig := proc .agent .CloneConfig (dc )
1097
1114
if proc .agent .ID == uuid .Nil || maybeRecreateSubAgent {
1115
+ subAgentConfig .Architecture = arch
1116
+
1098
1117
// Detect workspace folder by executing `pwd` in the container.
1099
1118
// NOTE(mafredri): This is a quick and dirty way to detect the
1100
1119
// workspace folder inside the container. In the future we will
@@ -1127,9 +1146,50 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1127
1146
codersdk .DisplayAppPortForward : true ,
1128
1147
}
1129
1148
1130
- if config , err := api .dccli .ReadConfig (ctx , dc .WorkspaceFolder , dc .ConfigPath ); err != nil {
1131
- api .logger .Error (ctx , "unable to read devcontainer config" , slog .Error (err ))
1132
- } else {
1149
+ var appsWithPossibleDuplicates []SubAgentApp
1150
+
1151
+ if err := func () error {
1152
+ var (
1153
+ config DevcontainerConfig
1154
+ configOutdated bool
1155
+ )
1156
+
1157
+ readConfig := func () (DevcontainerConfig , error ) {
1158
+ return api .dccli .ReadConfig (ctx , dc .WorkspaceFolder , dc .ConfigPath , []string {
1159
+ fmt .Sprintf ("CODER_WORKSPACE_AGENT_NAME=%s" , subAgentConfig .Name ),
1160
+ fmt .Sprintf ("CODER_WORKSPACE_OWNER_NAME=%s" , api .ownerName ),
1161
+ fmt .Sprintf ("CODER_WORKSPACE_NAME=%s" , api .workspaceName ),
1162
+ fmt .Sprintf ("CODER_URL=%s" , api .subAgentURL ),
1163
+ })
1164
+ }
1165
+
1166
+ if config , err = readConfig (); err != nil {
1167
+ return err
1168
+ }
1169
+
1170
+ // NOTE(DanielleMaywood):
1171
+ // We only want to take an agent name specified in the root customization layer.
1172
+ // This restricts the ability for a feature to specify the agent name. We may revisit
1173
+ // this in the future, but for now we want to restrict this behavior.
1174
+ if name := config .Configuration .Customizations .Coder .Name ; name != "" {
1175
+ // We only want to pick this name if it is a valid name.
1176
+ if provisioner .AgentNameRegex .Match ([]byte (name )) {
1177
+ subAgentConfig .Name = name
1178
+ configOutdated = true
1179
+ } else {
1180
+ logger .Warn (ctx , "invalid name in devcontainer customization, ignoring" ,
1181
+ slog .F ("name" , name ),
1182
+ slog .F ("regex" , provisioner .AgentNameRegex .String ()),
1183
+ )
1184
+ }
1185
+ }
1186
+
1187
+ if configOutdated {
1188
+ if config , err = readConfig (); err != nil {
1189
+ return err
1190
+ }
1191
+ }
1192
+
1133
1193
coderCustomization := config .MergedConfiguration .Customizations .Coder
1134
1194
1135
1195
for _ , customization := range coderCustomization {
@@ -1143,7 +1203,13 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1143
1203
}
1144
1204
displayAppsMap [app ] = enabled
1145
1205
}
1206
+
1207
+ appsWithPossibleDuplicates = append (appsWithPossibleDuplicates , customization .Apps ... )
1146
1208
}
1209
+
1210
+ return nil
1211
+ }(); err != nil {
1212
+ api .logger .Error (ctx , "unable to read devcontainer config" , slog .Error (err ))
1147
1213
}
1148
1214
1149
1215
displayApps := make ([]codersdk.DisplayApp , 0 , len (displayAppsMap ))
@@ -1154,7 +1220,27 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1154
1220
}
1155
1221
slices .Sort (displayApps )
1156
1222
1223
+ appSlugs := make (map [string ]struct {})
1224
+ apps := make ([]SubAgentApp , 0 , len (appsWithPossibleDuplicates ))
1225
+
1226
+ // We want to deduplicate the apps based on their slugs here.
1227
+ // As we want to prioritize later apps, we will walk through this
1228
+ // backwards.
1229
+ for _ , app := range slices .Backward (appsWithPossibleDuplicates ) {
1230
+ if _ , slugAlreadyExists := appSlugs [app .Slug ]; slugAlreadyExists {
1231
+ continue
1232
+ }
1233
+
1234
+ appSlugs [app .Slug ] = struct {}{}
1235
+ apps = append (apps , app )
1236
+ }
1237
+
1238
+ // Apps is currently in reverse order here, so by reversing it we restore
1239
+ // it to the original order.
1240
+ slices .Reverse (apps )
1241
+
1157
1242
subAgentConfig .DisplayApps = displayApps
1243
+ subAgentConfig .Apps = apps
1158
1244
}
1159
1245
1160
1246
deleteSubAgent := proc .agent .ID != uuid .Nil && maybeRecreateSubAgent && ! proc .agent .EqualConfig (subAgentConfig )
0 commit comments