func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWaiter, error) {
	if id == "" {
		return nil, &NoSuchExec{ID: id}
	}
	path := fmt.Sprintf("/exec/%s/start", id)
	if opts.Detach {
		resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
		if err != nil {
			if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
				return nil, &NoSuchExec{ID: id}
			}
			return nil, err
		}
		defer resp.Body.Close()
		return nil, nil
	}
	return c.hijack("POST", path, hijackOptions{
		success:        opts.Success,
		setRawTerminal: opts.RawTerminal,
		in:             opts.InputStream,
		stdout:         opts.OutputStream,
		stderr:         opts.ErrorStream,
		data:           opts,
	})
}
StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is
true, it returns after starting the exec command. Otherwise, it sets up an
interactive session with the exec command.

See https://goo.gl/1EeDWi for more details
package docker
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
)
type Exec struct {
	ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
}
type CreateExecOptions struct {
	AttachStdin  bool            `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
	AttachStdout bool            `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"`
	AttachStderr bool            `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"`
	Tty          bool            `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
	Env          []string        `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"`
	Cmd          []string        `json:"Cmd,omitempty" yaml:"Cmd,omitempty" toml:"Cmd,omitempty"`
	Container    string          `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
	User         string          `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"`
	WorkingDir   string          `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty" toml:"WorkingDir,omitempty"`
	Context      context.Context `json:"-"`
	Privileged   bool            `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
}
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
	if len(opts.Env) > 0 && c.serverAPIVersion.LessThan(apiVersion125) {
		return nil, errors.New("exec configuration Env is only supported in API#1.25 and above")
	}
	if len(opts.WorkingDir) > 0 && c.serverAPIVersion.LessThan(apiVersion135) {
		return nil, errors.New("exec configuration WorkingDir is only supported in API#1.35 and above")
	}
	path := fmt.Sprintf("/containers/%s/exec", opts.Container)
	resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
	if err != nil {
		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
			return nil, &NoSuchContainer{ID: opts.Container}
		}
		return nil, err
	}
	defer resp.Body.Close()
	var exec Exec
	if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
		return nil, err
	}
	return &exec, nil
}
type StartExecOptions struct {
	InputStream  io.Reader `qs:"-"`
	OutputStream io.Writer `qs:"-"`
	ErrorStream  io.Writer `qs:"-"`
	Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty" toml:"Detach,omitempty"`
	Tty    bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
	RawTerminal bool `qs:"-"`
	Success chan struct{} `json:"-"`
	Context context.Context `json:"-"`
}
func (c *Client) StartExec(id string, opts StartExecOptions) error {
	cw, err := c.StartExecNonBlocking(id, opts)
	if err != nil {
		return err
	}
	if cw != nil {
		return cw.Wait()
	}
	return nil
}