package docker import ( "bytes" "context" "fmt" "io" "github.com/moby/moby/client" ) // ExecInContainer runs a command inside a running container and returns the combined output. func (c *Client) ExecInContainer(ctx context.Context, containerID string, cmd []string) (string, error) { exec, err := c.api.ExecCreate(ctx, containerID, client.ExecCreateOptions{ Cmd: cmd, AttachStdout: true, AttachStderr: true, }) if err != nil { return "", fmt.Errorf("exec create: %w", err) } attach, err := c.api.ExecAttach(ctx, exec.ID, client.ExecAttachOptions{}) if err != nil { return "", fmt.Errorf("exec attach: %w", err) } defer attach.Close() var buf bytes.Buffer if _, err := io.Copy(&buf, attach.Reader); err != nil { return "", fmt.Errorf("exec read output: %w", err) } // Check exit code. inspect, err := c.api.ExecInspect(ctx, exec.ID, client.ExecInspectOptions{}) if err != nil { return buf.String(), nil // best effort — return output even if inspect fails } if inspect.ExitCode != 0 { return "", fmt.Errorf("exec exited with code %d", inspect.ExitCode) } return buf.String(), nil }