code: func Exec(ctx context.Context, cli client.APIClient, id string, cmd []string) (ExecResult, error) {
  	// prepare exec
  	execConfig := types.ExecConfig{
  		AttachStdout: true,
  		AttachStderr: true,
  		Cmd:          cmd,
  	}
  	cresp, err := cli.ContainerExecCreate(ctx, id, execConfig)
  	if err != nil {
  		return ExecResult{}, err
  	}
  	execID := cresp.ID
  
  	// run it, with stdout/stderr attached
  	aresp, err := cli.ContainerExecAttach(ctx, execID, types.ExecStartCheck{})
  	if err != nil {
  		return ExecResult{}, err
  	}
  	defer aresp.Close()
  
  	// read the output
  	var outBuf, errBuf bytes.Buffer
  	outputDone := make(chan error)
  
  	go func() {
  		// StdCopy demultiplexes the stream into two buffers
  		_, err = stdcopy.StdCopy(&outBuf, &errBuf, aresp.Reader)
  		outputDone <- err
  	}()
  
  	select {
  	case err := <-outputDone:
  		if err != nil {
  			return ExecResult{}, err
  		}
  		break
  
  	case <-ctx.Done():
  		return ExecResult{}, ctx.Err()
  	}
  
  	// get the exit code
  	iresp, err := cli.ContainerExecInspect(ctx, execID)
  	if err != nil {
  		return ExecResult{}, err
  	}
  
  	return ExecResult{ExitCode: iresp.ExitCode, outBuffer: &outBuf, errBuffer: &errBuf}, nil
  }
docs: // Exec executes a command inside a container, returning the result
  // containing stdout, stderr, and exit code. Note:
  //  - this is a synchronous operation;
  //  - cmd stdin is closed.