// SPDX-License-Identifier: GPL-2.0-or-later // Copyright (C) 2026 Bryan Joshua Pedini package main import ( "context" "fmt" "log/slog" "time" "github.com/moby/moby/api/types/container" "github.com/moby/moby/client" ) // dockerHost is the Docker Engine endpoint used in production const dockerHost = "unix:///var/run/docker.sock" // dockerListTimeout bounds every container-list call (the SDK has no default timeout) const dockerListTimeout = 10 * time.Second // ContainerLister is the narrow Docker dependency of the HTTP layer type ContainerLister interface { ListRunningContainers(ctx context.Context) ([]container.Summary, error) } // DockerClient wraps the Docker Engine SDK client type DockerClient struct { cli *client.Client log *slog.Logger timeout time.Duration } // NewDockerClient creates a client for the given Engine host (e.g. unix:///var/run/docker.sock) // The connection is lazy: an absent or inaccessible socket only fails individual requests func NewDockerClient(host string, log *slog.Logger) (*DockerClient, error) { cli, err := client.New(client.WithHost(host)) if err != nil { return nil, fmt.Errorf("create docker client: %w", err) } return &DockerClient{cli: cli, log: log, timeout: dockerListTimeout}, nil } // ListRunningContainers returns the running containers, like `docker ps` // The result is never nil, so it serializes as [] when empty func (d *DockerClient) ListRunningContainers(ctx context.Context) ([]container.Summary, error) { defer traceFunc(ctx, d.log, "DockerClient.ListRunningContainers")() ctx, cancel := context.WithTimeout(ctx, d.timeout) defer cancel() // All=false: running containers only res, err := d.cli.ContainerList(ctx, client.ContainerListOptions{}) if err != nil { return nil, fmt.Errorf("list containers: %w", err) } items := res.Items if items == nil { items = []container.Summary{} } for _, c := range items { trace(ctx, d.log, "container listed", "id", c.ID, "names", c.Names, "image", c.Image, "state", c.State) } return items, nil } // Close releases the client's resources func (d *DockerClient) Close() error { return d.cli.Close() }