@@ -162,23 +162,28 @@ func (dei *DockerEnvInfoer) ModifyCommand(cmd string, args ...string) (string, [
162
162
// devcontainerEnv is a helper function that inspects the container labels to
163
163
// find the required environment variables for running a command in the container.
164
164
func devcontainerEnv (ctx context.Context ,execer agentexec.Execer ,container string ) ([]string ,error ) {
165
- ins ,stderr ,err := runDockerInspect (ctx ,execer ,container )
165
+ stdout ,stderr ,err := runDockerInspect (ctx ,execer ,container )
166
166
if err != nil {
167
167
return nil ,xerrors .Errorf ("inspect container: %w: %q" ,err ,stderr )
168
168
}
169
169
170
+ ins ,_ ,err := convertDockerInspect (stdout )
171
+ if err != nil {
172
+ return nil ,xerrors .Errorf ("inspect container: %w" ,err )
173
+ }
174
+
170
175
if len (ins )!= 1 {
171
176
return nil ,xerrors .Errorf ("inspect container: expected 1 container, got %d" ,len (ins ))
172
177
}
173
178
174
179
in := ins [0 ]
175
- if in .Config . Labels == nil {
180
+ if in .Labels == nil {
176
181
return nil ,nil
177
182
}
178
183
179
184
// We want to look for the devcontainer metadata, which is in the
180
185
// value of the label `devcontainer.metadata`.
181
- rawMeta ,ok := in .Config . Labels ["devcontainer.metadata" ]
186
+ rawMeta ,ok := in .Labels ["devcontainer.metadata" ]
182
187
if ! ok {
183
188
return nil ,nil
184
189
}
@@ -279,42 +284,36 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
279
284
return codersdk.WorkspaceAgentListContainersResponse {},xerrors .Errorf ("run docker inspect: %w" ,err )
280
285
}
281
286
282
- for _ ,in := range ins {
283
- out ,warns := convertDockerInspect (in )
284
- res .Warnings = append (res .Warnings ,warns ... )
285
- res .Containers = append (res .Containers ,out )
286
- }
287
-
288
- if dockerPsStderr != "" {
289
- res .Warnings = append (res .Warnings ,dockerPsStderr )
290
- }
291
287
if dockerInspectStderr != "" {
292
288
res .Warnings = append (res .Warnings ,dockerInspectStderr )
293
289
}
294
290
291
+ outs ,warns ,err := convertDockerInspect (ins )
292
+ if err != nil {
293
+ return codersdk.WorkspaceAgentListContainersResponse {},xerrors .Errorf ("convert docker inspect output: %w" ,err )
294
+ }
295
+ res .Warnings = append (res .Warnings ,warns ... )
296
+ res .Containers = append (res .Containers ,outs ... )
297
+
295
298
return res ,nil
296
299
}
297
300
298
301
// runDockerInspect is a helper function that runs `docker inspect` on the given
299
302
// container IDs and returns the parsed output.
300
303
// The stderr output is also returned for logging purposes.
301
- func runDockerInspect (ctx context.Context ,execer agentexec.Execer ,ids ... string ) ([] dockerInspect , string ,error ) {
304
+ func runDockerInspect (ctx context.Context ,execer agentexec.Execer ,ids ... string ) (stdout , stderr string ,err error ) {
302
305
var stdoutBuf ,stderrBuf bytes.Buffer
303
306
cmd := execer .CommandContext (ctx ,"docker" ,append ([]string {"inspect" },ids ... )... )
304
307
cmd .Stdout = & stdoutBuf
305
308
cmd .Stderr = & stderrBuf
306
- err := cmd .Run ()
307
- stderr := strings .TrimSpace (stderrBuf .String ())
309
+ err = cmd .Run ()
310
+ stdout = strings .TrimSpace (stdoutBuf .String ())
311
+ stderr = strings .TrimSpace (stderrBuf .String ())
308
312
if err != nil {
309
- return nil ,stderr ,err
313
+ return stdout ,stderr ,err
310
314
}
311
315
312
- var ins []dockerInspect
313
- if err := json .NewDecoder (& stdoutBuf ).Decode (& ins );err != nil {
314
- return nil ,stderr ,xerrors .Errorf ("decode docker inspect output: %w" ,err )
315
- }
316
-
317
- return ins ,stderr ,nil
316
+ return stdoutBuf .String (),stderr ,nil
318
317
}
319
318
320
319
// To avoid a direct dependency on the Docker API, we use the docker CLI
@@ -367,50 +366,59 @@ func (dis dockerInspectState) String() string {
367
366
return sb .String ()
368
367
}
369
368
370
- func convertDockerInspect (in dockerInspect ) (codersdk.WorkspaceAgentDevcontainer , []string ) {
369
+ func convertDockerInspect (raw string ) ([] codersdk.WorkspaceAgentDevcontainer , []string , error ) {
371
370
var warns []string
372
- out := codersdk.WorkspaceAgentDevcontainer {
373
- CreatedAt :in .Created ,
374
- // Remove the leading slash from the container name
375
- FriendlyName :strings .TrimPrefix (in .Name ,"/" ),
376
- ID :in .ID ,
377
- Image :in .Config .Image ,
378
- Labels :in .Config .Labels ,
379
- Ports :make ([]codersdk.WorkspaceAgentListeningPort ,0 ),
380
- Running :in .State .Running ,
381
- Status :in .State .String (),
382
- Volumes :make (map [string ]string ,len (in .Mounts )),
383
- }
384
-
385
- if in .HostConfig .PortBindings == nil {
386
- in .HostConfig .PortBindings = make (map [string ]any )
387
- }
388
- portKeys := maps .Keys (in .HostConfig .PortBindings )
389
- // Sort the ports for deterministic output.
390
- sort .Strings (portKeys )
391
- for _ ,p := range portKeys {
392
- if port ,network ,err := convertDockerPort (p );err != nil {
393
- warns = append (warns ,err .Error ())
394
- }else {
395
- out .Ports = append (out .Ports , codersdk.WorkspaceAgentListeningPort {
396
- Network :network ,
397
- Port :port ,
398
- })
399
- }
371
+ var ins []dockerInspect
372
+ if err := json .NewDecoder (strings .NewReader (raw )).Decode (& ins );err != nil {
373
+ return nil ,nil ,xerrors .Errorf ("decode docker inspect output: %w" ,err )
400
374
}
375
+ outs := make ([]codersdk.WorkspaceAgentDevcontainer ,0 ,len (ins ))
401
376
402
- if in .Mounts == nil {
403
- in .Mounts = []dockerInspectMount {}
404
- }
405
- // Sort the mounts for deterministic output.
406
- sort .Slice (in .Mounts ,func (i ,j int )bool {
407
- return in .Mounts [i ].Source < in .Mounts [j ].Source
408
- })
409
- for _ ,k := range in .Mounts {
410
- out .Volumes [k .Source ]= k .Destination
377
+ for _ ,in := range ins {
378
+ out := codersdk.WorkspaceAgentDevcontainer {
379
+ CreatedAt :in .Created ,
380
+ // Remove the leading slash from the container name
381
+ FriendlyName :strings .TrimPrefix (in .Name ,"/" ),
382
+ ID :in .ID ,
383
+ Image :in .Config .Image ,
384
+ Labels :in .Config .Labels ,
385
+ Ports :make ([]codersdk.WorkspaceAgentListeningPort ,0 ),
386
+ Running :in .State .Running ,
387
+ Status :in .State .String (),
388
+ Volumes :make (map [string ]string ,len (in .Mounts )),
389
+ }
390
+
391
+ if in .HostConfig .PortBindings == nil {
392
+ in .HostConfig .PortBindings = make (map [string ]any )
393
+ }
394
+ portKeys := maps .Keys (in .HostConfig .PortBindings )
395
+ // Sort the ports for deterministic output.
396
+ sort .Strings (portKeys )
397
+ for _ ,p := range portKeys {
398
+ if port ,network ,err := convertDockerPort (p );err != nil {
399
+ warns = append (warns ,err .Error ())
400
+ }else {
401
+ out .Ports = append (out .Ports , codersdk.WorkspaceAgentListeningPort {
402
+ Network :network ,
403
+ Port :port ,
404
+ })
405
+ }
406
+ }
407
+
408
+ if in .Mounts == nil {
409
+ in .Mounts = []dockerInspectMount {}
410
+ }
411
+ // Sort the mounts for deterministic output.
412
+ sort .Slice (in .Mounts ,func (i ,j int )bool {
413
+ return in .Mounts [i ].Source < in .Mounts [j ].Source
414
+ })
415
+ for _ ,k := range in .Mounts {
416
+ out .Volumes [k .Source ]= k .Destination
417
+ }
418
+ outs = append (outs ,out )
411
419
}
412
420
413
- return out ,warns
421
+ return outs ,warns , nil
414
422
}
415
423
416
424
// convertDockerPort converts a Docker port string to a port number and network