(ctx context.Context)
| 242 | } |
| 243 | |
| 244 | func (dcli *dockerCLI) List(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) { |
| 245 | var stdoutBuf, stderrBuf bytes.Buffer |
| 246 | // List all container IDs, one per line, with no truncation |
| 247 | cmd := dcli.execer.CommandContext(ctx, "docker", "ps", "--all", "--quiet", "--no-trunc") |
| 248 | cmd.Stdout = &stdoutBuf |
| 249 | cmd.Stderr = &stderrBuf |
| 250 | if err := cmd.Run(); err != nil { |
| 251 | // TODO(Cian): detect specific errors: |
| 252 | // - docker not installed |
| 253 | // - docker not running |
| 254 | // - no permissions to talk to docker |
| 255 | return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("run docker ps: %w: %q", err, strings.TrimSpace(stderrBuf.String())) |
| 256 | } |
| 257 | |
| 258 | ids := make([]string, 0) |
| 259 | scanner := bufio.NewScanner(&stdoutBuf) |
| 260 | for scanner.Scan() { |
| 261 | tmp := strings.TrimSpace(scanner.Text()) |
| 262 | if tmp == "" { |
| 263 | continue |
| 264 | } |
| 265 | ids = append(ids, tmp) |
| 266 | } |
| 267 | if err := scanner.Err(); err != nil { |
| 268 | return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("scan docker ps output: %w", err) |
| 269 | } |
| 270 | |
| 271 | res := codersdk.WorkspaceAgentListContainersResponse{ |
| 272 | Containers: make([]codersdk.WorkspaceAgentContainer, 0, len(ids)), |
| 273 | Warnings: make([]string, 0), |
| 274 | } |
| 275 | dockerPsStderr := strings.TrimSpace(stderrBuf.String()) |
| 276 | if dockerPsStderr != "" { |
| 277 | res.Warnings = append(res.Warnings, dockerPsStderr) |
| 278 | } |
| 279 | if len(ids) == 0 { |
| 280 | return res, nil |
| 281 | } |
| 282 | |
| 283 | // now we can get the detailed information for each container |
| 284 | // Run `docker inspect` on each container ID. |
| 285 | // NOTE: There is an unavoidable potential race condition where a |
| 286 | // container is removed between `docker ps` and `docker inspect`. |
| 287 | // In this case, stderr will contain an error message but stdout |
| 288 | // will still contain valid JSON. We will just end up missing |
| 289 | // information about the removed container. We could potentially |
| 290 | // log this error, but I'm not sure it's worth it. |
| 291 | dockerInspectStdout, dockerInspectStderr, err := runDockerInspect(ctx, dcli.execer, ids...) |
| 292 | if err != nil { |
| 293 | return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("run docker inspect: %w: %s", err, dockerInspectStderr) |
| 294 | } |
| 295 | |
| 296 | if len(dockerInspectStderr) > 0 { |
| 297 | res.Warnings = append(res.Warnings, string(dockerInspectStderr)) |
| 298 | } |
| 299 | |
| 300 | outs, warns, err := convertDockerInspect(dockerInspectStdout) |
| 301 | if err != nil { |
nothing calls this directly
no test coverage detected