package docker import ( "context" "encoding/json" "io" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" ) type Client struct { cli *client.Client ctx context.Context cancel context.CancelFunc } func New(host ...string) (*Client, error) { opts := []client.Opt{client.FromEnv, client.WithAPIVersionNegotiation()} if len(host) > 0 && host[0] != "" { opts = append(opts, client.WithHost(host[0])) } cli, err := client.NewClientWithOpts(opts...) if err != nil { return nil, err } ctx, cancel := context.WithCancel(context.Background()) return &Client{cli: cli, ctx: ctx, cancel: cancel}, nil } // Cancel cancels all in-flight Docker API calls. func (c *Client) Cancel() { c.cancel() } func (c *Client) Ping() error { _, err := c.cli.Ping(c.ctx) return err } func (c *Client) ListContainers(all bool) ([]types.Container, error) { opts := container.ListOptions{All: all} return c.cli.ContainerList(c.ctx, opts) } func (c *Client) InspectContainer(id string) (types.ContainerJSON, error) { return c.cli.ContainerInspect(c.ctx, id) } func (c *Client) StartContainer(id string) error { return c.cli.ContainerStart(c.ctx, id, container.StartOptions{}) } func (c *Client) StopContainer(id string) error { return c.cli.ContainerStop(c.ctx, id, container.StopOptions{}) } func (c *Client) RestartContainer(id string) error { return c.cli.ContainerRestart(c.ctx, id, container.StopOptions{}) } func (c *Client) DeleteContainer(id string, force bool) error { return c.cli.ContainerRemove(c.ctx, id, container.RemoveOptions{Force: force}) } func (c *Client) PauseContainer(id string) error { return c.cli.ContainerPause(c.ctx, id) } func (c *Client) UnpauseContainer(id string) error { return c.cli.ContainerUnpause(c.ctx, id) } func (c *Client) Logs(id string, options container.LogsOptions) (io.ReadCloser, error) { return c.cli.ContainerLogs(c.ctx, id, options) } // ContainerStats gets a single stats snapshot for a container. // Uses stream=false (NOT one-shot) so Docker fills in precpu_stats for accurate CPU delta. func (c *Client) ContainerStats(id string) (*container.StatsResponse, error) { resp, err := c.cli.ContainerStats(c.ctx, id, false) if err != nil { return nil, err } defer resp.Body.Close() var stats container.StatsResponse if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil { return nil, err } return &stats, nil } func (c *Client) ContainerExec(id string, cmd []string, tty bool) (types.HijackedResponse, error) { execConfig := container.ExecOptions{ Cmd: cmd, Tty: tty, AttachStdin: true, AttachStdout: true, AttachStderr: true, } exec, err := c.cli.ContainerExecCreate(c.ctx, id, execConfig) if err != nil { return types.HijackedResponse{}, err } return c.cli.ContainerExecAttach(c.ctx, exec.ID, container.ExecStartOptions{}) } func (c *Client) Close() error { return c.cli.Close() }