1// Copyright 2013 go-dockerclient authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package docker
6
7import (
8	"context"
9	"encoding/json"
10	"errors"
11	"fmt"
12	"io"
13	"net/http"
14	"net/url"
15	"strconv"
16	"strings"
17	"time"
18
19	"github.com/docker/go-units"
20)
21
22// ErrContainerAlreadyExists is the error returned by CreateContainer when the
23// container already exists.
24var ErrContainerAlreadyExists = errors.New("container already exists")
25
26// ListContainersOptions specify parameters to the ListContainers function.
27//
28// See https://goo.gl/kaOHGw for more details.
29type ListContainersOptions struct {
30	All     bool
31	Size    bool
32	Limit   int
33	Since   string
34	Before  string
35	Filters map[string][]string
36	Context context.Context
37}
38
39// APIPort is a type that represents a port mapping returned by the Docker API
40type APIPort struct {
41	PrivatePort int64  `json:"PrivatePort,omitempty" yaml:"PrivatePort,omitempty" toml:"PrivatePort,omitempty"`
42	PublicPort  int64  `json:"PublicPort,omitempty" yaml:"PublicPort,omitempty" toml:"PublicPort,omitempty"`
43	Type        string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
44	IP          string `json:"IP,omitempty" yaml:"IP,omitempty" toml:"IP,omitempty"`
45}
46
47// APIMount represents a mount point for a container.
48type APIMount struct {
49	Name        string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
50	Source      string `json:"Source,omitempty" yaml:"Source,omitempty" toml:"Source,omitempty"`
51	Destination string `json:"Destination,omitempty" yaml:"Destination,omitempty" toml:"Destination,omitempty"`
52	Driver      string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"`
53	Mode        string `json:"Mode,omitempty" yaml:"Mode,omitempty" toml:"Mode,omitempty"`
54	RW          bool   `json:"RW,omitempty" yaml:"RW,omitempty" toml:"RW,omitempty"`
55	Propogation string `json:"Propogation,omitempty" yaml:"Propogation,omitempty" toml:"Propogation,omitempty"`
56}
57
58// APIContainers represents each container in the list returned by
59// ListContainers.
60type APIContainers struct {
61	ID         string            `json:"Id" yaml:"Id" toml:"Id"`
62	Image      string            `json:"Image,omitempty" yaml:"Image,omitempty" toml:"Image,omitempty"`
63	Command    string            `json:"Command,omitempty" yaml:"Command,omitempty" toml:"Command,omitempty"`
64	Created    int64             `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
65	State      string            `json:"State,omitempty" yaml:"State,omitempty" toml:"State,omitempty"`
66	Status     string            `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"`
67	Ports      []APIPort         `json:"Ports,omitempty" yaml:"Ports,omitempty" toml:"Ports,omitempty"`
68	SizeRw     int64             `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty" toml:"SizeRw,omitempty"`
69	SizeRootFs int64             `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty" toml:"SizeRootFs,omitempty"`
70	Names      []string          `json:"Names,omitempty" yaml:"Names,omitempty" toml:"Names,omitempty"`
71	Labels     map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
72	Networks   NetworkList       `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty" toml:"NetworkSettings,omitempty"`
73	Mounts     []APIMount        `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"`
74}
75
76// NetworkList encapsulates a map of networks, as returned by the Docker API in
77// ListContainers.
78type NetworkList struct {
79	Networks map[string]ContainerNetwork `json:"Networks" yaml:"Networks,omitempty" toml:"Networks,omitempty"`
80}
81
82// ListContainers returns a slice of containers matching the given criteria.
83//
84// See https://goo.gl/kaOHGw for more details.
85func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
86	path := "/containers/json?" + queryString(opts)
87	resp, err := c.do("GET", path, doOptions{context: opts.Context})
88	if err != nil {
89		return nil, err
90	}
91	defer resp.Body.Close()
92	var containers []APIContainers
93	if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil {
94		return nil, err
95	}
96	return containers, nil
97}
98
99// Port represents the port number and the protocol, in the form
100// <number>/<protocol>. For example: 80/tcp.
101type Port string
102
103// Port returns the number of the port.
104func (p Port) Port() string {
105	return strings.Split(string(p), "/")[0]
106}
107
108// Proto returns the name of the protocol.
109func (p Port) Proto() string {
110	parts := strings.Split(string(p), "/")
111	if len(parts) == 1 {
112		return "tcp"
113	}
114	return parts[1]
115}
116
117// HealthCheck represents one check of health.
118type HealthCheck struct {
119	Start    time.Time `json:"Start,omitempty" yaml:"Start,omitempty" toml:"Start,omitempty"`
120	End      time.Time `json:"End,omitempty" yaml:"End,omitempty" toml:"End,omitempty"`
121	ExitCode int       `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"`
122	Output   string    `json:"Output,omitempty" yaml:"Output,omitempty" toml:"Output,omitempty"`
123}
124
125// Health represents the health of a container.
126type Health struct {
127	Status        string        `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"`
128	FailingStreak int           `json:"FailingStreak,omitempty" yaml:"FailingStreak,omitempty" toml:"FailingStreak,omitempty"`
129	Log           []HealthCheck `json:"Log,omitempty" yaml:"Log,omitempty" toml:"Log,omitempty"`
130}
131
132// State represents the state of a container.
133type State struct {
134	Status            string    `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"`
135	Running           bool      `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"`
136	Paused            bool      `json:"Paused,omitempty" yaml:"Paused,omitempty" toml:"Paused,omitempty"`
137	Restarting        bool      `json:"Restarting,omitempty" yaml:"Restarting,omitempty" toml:"Restarting,omitempty"`
138	OOMKilled         bool      `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty" toml:"OOMKilled,omitempty"`
139	RemovalInProgress bool      `json:"RemovalInProgress,omitempty" yaml:"RemovalInProgress,omitempty" toml:"RemovalInProgress,omitempty"`
140	Dead              bool      `json:"Dead,omitempty" yaml:"Dead,omitempty" toml:"Dead,omitempty"`
141	Pid               int       `json:"Pid,omitempty" yaml:"Pid,omitempty" toml:"Pid,omitempty"`
142	ExitCode          int       `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"`
143	Error             string    `json:"Error,omitempty" yaml:"Error,omitempty" toml:"Error,omitempty"`
144	StartedAt         time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty" toml:"StartedAt,omitempty"`
145	FinishedAt        time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty" toml:"FinishedAt,omitempty"`
146	Health            Health    `json:"Health,omitempty" yaml:"Health,omitempty" toml:"Health,omitempty"`
147}
148
149// String returns a human-readable description of the state
150func (s *State) String() string {
151	if s.Running {
152		if s.Paused {
153			return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
154		}
155		if s.Restarting {
156			return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
157		}
158
159		return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
160	}
161
162	if s.RemovalInProgress {
163		return "Removal In Progress"
164	}
165
166	if s.Dead {
167		return "Dead"
168	}
169
170	if s.StartedAt.IsZero() {
171		return "Created"
172	}
173
174	if s.FinishedAt.IsZero() {
175		return ""
176	}
177
178	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
179}
180
181// StateString returns a single string to describe state
182func (s *State) StateString() string {
183	if s.Running {
184		if s.Paused {
185			return "paused"
186		}
187		if s.Restarting {
188			return "restarting"
189		}
190		return "running"
191	}
192
193	if s.Dead {
194		return "dead"
195	}
196
197	if s.StartedAt.IsZero() {
198		return "created"
199	}
200
201	return "exited"
202}
203
204// PortBinding represents the host/container port mapping as returned in the
205// `docker inspect` json
206type PortBinding struct {
207	HostIP   string `json:"HostIp,omitempty" yaml:"HostIp,omitempty" toml:"HostIp,omitempty"`
208	HostPort string `json:"HostPort,omitempty" yaml:"HostPort,omitempty" toml:"HostPort,omitempty"`
209}
210
211// PortMapping represents a deprecated field in the `docker inspect` output,
212// and its value as found in NetworkSettings should always be nil
213type PortMapping map[string]string
214
215// ContainerNetwork represents the networking settings of a container per network.
216type ContainerNetwork struct {
217	Aliases             []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty" toml:"Aliases,omitempty"`
218	MacAddress          string   `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"`
219	GlobalIPv6PrefixLen int      `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"`
220	GlobalIPv6Address   string   `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"`
221	IPv6Gateway         string   `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"`
222	IPPrefixLen         int      `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"`
223	IPAddress           string   `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"`
224	Gateway             string   `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"`
225	EndpointID          string   `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"`
226	NetworkID           string   `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"`
227}
228
229// NetworkSettings contains network-related information about a container
230type NetworkSettings struct {
231	Networks               map[string]ContainerNetwork `json:"Networks,omitempty" yaml:"Networks,omitempty" toml:"Networks,omitempty"`
232	IPAddress              string                      `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"`
233	IPPrefixLen            int                         `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"`
234	MacAddress             string                      `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"`
235	Gateway                string                      `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"`
236	Bridge                 string                      `json:"Bridge,omitempty" yaml:"Bridge,omitempty" toml:"Bridge,omitempty"`
237	PortMapping            map[string]PortMapping      `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty" toml:"PortMapping,omitempty"`
238	Ports                  map[Port][]PortBinding      `json:"Ports,omitempty" yaml:"Ports,omitempty" toml:"Ports,omitempty"`
239	NetworkID              string                      `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"`
240	EndpointID             string                      `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"`
241	SandboxKey             string                      `json:"SandboxKey,omitempty" yaml:"SandboxKey,omitempty" toml:"SandboxKey,omitempty"`
242	GlobalIPv6Address      string                      `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"`
243	GlobalIPv6PrefixLen    int                         `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"`
244	IPv6Gateway            string                      `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"`
245	LinkLocalIPv6Address   string                      `json:"LinkLocalIPv6Address,omitempty" yaml:"LinkLocalIPv6Address,omitempty" toml:"LinkLocalIPv6Address,omitempty"`
246	LinkLocalIPv6PrefixLen int                         `json:"LinkLocalIPv6PrefixLen,omitempty" yaml:"LinkLocalIPv6PrefixLen,omitempty" toml:"LinkLocalIPv6PrefixLen,omitempty"`
247	SecondaryIPAddresses   []string                    `json:"SecondaryIPAddresses,omitempty" yaml:"SecondaryIPAddresses,omitempty" toml:"SecondaryIPAddresses,omitempty"`
248	SecondaryIPv6Addresses []string                    `json:"SecondaryIPv6Addresses,omitempty" yaml:"SecondaryIPv6Addresses,omitempty" toml:"SecondaryIPv6Addresses,omitempty"`
249}
250
251// PortMappingAPI translates the port mappings as contained in NetworkSettings
252// into the format in which they would appear when returned by the API
253func (settings *NetworkSettings) PortMappingAPI() []APIPort {
254	var mapping []APIPort
255	for port, bindings := range settings.Ports {
256		p, _ := parsePort(port.Port())
257		if len(bindings) == 0 {
258			mapping = append(mapping, APIPort{
259				PrivatePort: int64(p),
260				Type:        port.Proto(),
261			})
262			continue
263		}
264		for _, binding := range bindings {
265			p, _ := parsePort(port.Port())
266			h, _ := parsePort(binding.HostPort)
267			mapping = append(mapping, APIPort{
268				PrivatePort: int64(p),
269				PublicPort:  int64(h),
270				Type:        port.Proto(),
271				IP:          binding.HostIP,
272			})
273		}
274	}
275	return mapping
276}
277
278func parsePort(rawPort string) (int, error) {
279	port, err := strconv.ParseUint(rawPort, 10, 16)
280	if err != nil {
281		return 0, err
282	}
283	return int(port), nil
284}
285
286// Config is the list of configuration options used when creating a container.
287// Config does not contain the options that are specific to starting a container on a
288// given host.  Those are contained in HostConfig
289type Config struct {
290	Hostname          string              `json:"Hostname,omitempty" yaml:"Hostname,omitempty" toml:"Hostname,omitempty"`
291	Domainname        string              `json:"Domainname,omitempty" yaml:"Domainname,omitempty" toml:"Domainname,omitempty"`
292	User              string              `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"`
293	Memory            int64               `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"`
294	MemorySwap        int64               `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty" toml:"MemorySwap,omitempty"`
295	MemoryReservation int64               `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty" toml:"MemoryReservation,omitempty"`
296	KernelMemory      int64               `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty" toml:"KernelMemory,omitempty"`
297	CPUShares         int64               `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty" toml:"CpuShares,omitempty"`
298	CPUSet            string              `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty" toml:"Cpuset,omitempty"`
299	PortSpecs         []string            `json:"PortSpecs,omitempty" yaml:"PortSpecs,omitempty" toml:"PortSpecs,omitempty"`
300	ExposedPorts      map[Port]struct{}   `json:"ExposedPorts,omitempty" yaml:"ExposedPorts,omitempty" toml:"ExposedPorts,omitempty"`
301	PublishService    string              `json:"PublishService,omitempty" yaml:"PublishService,omitempty" toml:"PublishService,omitempty"`
302	StopSignal        string              `json:"StopSignal,omitempty" yaml:"StopSignal,omitempty" toml:"StopSignal,omitempty"`
303	StopTimeout       int                 `json:"StopTimeout,omitempty" yaml:"StopTimeout,omitempty" toml:"StopTimeout,omitempty"`
304	Env               []string            `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"`
305	Cmd               []string            `json:"Cmd" yaml:"Cmd" toml:"Cmd"`
306	Shell             []string            `json:"Shell,omitempty" yaml:"Shell,omitempty" toml:"Shell,omitempty"`
307	Healthcheck       *HealthConfig       `json:"Healthcheck,omitempty" yaml:"Healthcheck,omitempty" toml:"Healthcheck,omitempty"`
308	DNS               []string            `json:"Dns,omitempty" yaml:"Dns,omitempty" toml:"Dns,omitempty"` // For Docker API v1.9 and below only
309	Image             string              `json:"Image,omitempty" yaml:"Image,omitempty" toml:"Image,omitempty"`
310	Volumes           map[string]struct{} `json:"Volumes,omitempty" yaml:"Volumes,omitempty" toml:"Volumes,omitempty"`
311	VolumeDriver      string              `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty" toml:"VolumeDriver,omitempty"`
312	WorkingDir        string              `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty" toml:"WorkingDir,omitempty"`
313	MacAddress        string              `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"`
314	Entrypoint        []string            `json:"Entrypoint" yaml:"Entrypoint" toml:"Entrypoint"`
315	SecurityOpts      []string            `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty" toml:"SecurityOpts,omitempty"`
316	OnBuild           []string            `json:"OnBuild,omitempty" yaml:"OnBuild,omitempty" toml:"OnBuild,omitempty"`
317	Mounts            []Mount             `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"`
318	Labels            map[string]string   `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
319	AttachStdin       bool                `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
320	AttachStdout      bool                `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"`
321	AttachStderr      bool                `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"`
322	ArgsEscaped       bool                `json:"ArgsEscaped,omitempty" yaml:"ArgsEscaped,omitempty" toml:"ArgsEscaped,omitempty"`
323	Tty               bool                `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
324	OpenStdin         bool                `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"`
325	StdinOnce         bool                `json:"StdinOnce,omitempty" yaml:"StdinOnce,omitempty" toml:"StdinOnce,omitempty"`
326	NetworkDisabled   bool                `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty" toml:"NetworkDisabled,omitempty"`
327
328	// This is no longer used and has been kept here for backward
329	// compatibility, please use HostConfig.VolumesFrom.
330	VolumesFrom string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty" toml:"VolumesFrom,omitempty"`
331}
332
333// HostMount represents a mount point in the container in HostConfig.
334//
335// It has been added in the version 1.25 of the Docker API
336type HostMount struct {
337	Target        string         `json:"Target,omitempty" yaml:"Target,omitempty" toml:"Target,omitempty"`
338	Source        string         `json:"Source,omitempty" yaml:"Source,omitempty" toml:"Source,omitempty"`
339	Type          string         `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
340	ReadOnly      bool           `json:"ReadOnly,omitempty" yaml:"ReadOnly,omitempty" toml:"ReadOnly,omitempty"`
341	BindOptions   *BindOptions   `json:"BindOptions,omitempty" yaml:"BindOptions,omitempty" toml:"BindOptions,omitempty"`
342	VolumeOptions *VolumeOptions `json:"VolumeOptions,omitempty" yaml:"VolumeOptions,omitempty" toml:"VolumeOptions,omitempty"`
343	TempfsOptions *TempfsOptions `json:"TempfsOptions,omitempty" yaml:"TempfsOptions,omitempty" toml:"TempfsOptions,omitempty"`
344}
345
346// BindOptions contains optional configuration for the bind type
347type BindOptions struct {
348	Propagation string `json:"Propagation,omitempty" yaml:"Propagation,omitempty" toml:"Propagation,omitempty"`
349}
350
351// VolumeOptions contains optional configuration for the volume type
352type VolumeOptions struct {
353	NoCopy       bool               `json:"NoCopy,omitempty" yaml:"NoCopy,omitempty" toml:"NoCopy,omitempty"`
354	Labels       map[string]string  `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
355	DriverConfig VolumeDriverConfig `json:"DriverConfig,omitempty" yaml:"DriverConfig,omitempty" toml:"DriverConfig,omitempty"`
356}
357
358// TempfsOptions contains optional configuration for the tempfs type
359type TempfsOptions struct {
360	SizeBytes int64 `json:"SizeBytes,omitempty" yaml:"SizeBytes,omitempty" toml:"SizeBytes,omitempty"`
361	Mode      int   `json:"Mode,omitempty" yaml:"Mode,omitempty" toml:"Mode,omitempty"`
362}
363
364// VolumeDriverConfig holds a map of volume driver specific options
365type VolumeDriverConfig struct {
366	Name    string            `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
367	Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"`
368}
369
370// Mount represents a mount point in the container.
371//
372// It has been added in the version 1.20 of the Docker API, available since
373// Docker 1.8.
374type Mount struct {
375	Name        string
376	Source      string
377	Destination string
378	Driver      string
379	Mode        string
380	RW          bool
381}
382
383// LogConfig defines the log driver type and the configuration for it.
384type LogConfig struct {
385	Type   string            `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
386	Config map[string]string `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
387}
388
389// ULimit defines system-wide resource limitations This can help a lot in
390// system administration, e.g. when a user starts too many processes and
391// therefore makes the system unresponsive for other users.
392type ULimit struct {
393	Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
394	Soft int64  `json:"Soft,omitempty" yaml:"Soft,omitempty" toml:"Soft,omitempty"`
395	Hard int64  `json:"Hard,omitempty" yaml:"Hard,omitempty" toml:"Hard,omitempty"`
396}
397
398// SwarmNode containers information about which Swarm node the container is on.
399type SwarmNode struct {
400	ID     string            `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"`
401	IP     string            `json:"IP,omitempty" yaml:"IP,omitempty" toml:"IP,omitempty"`
402	Addr   string            `json:"Addr,omitempty" yaml:"Addr,omitempty" toml:"Addr,omitempty"`
403	Name   string            `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
404	CPUs   int64             `json:"CPUs,omitempty" yaml:"CPUs,omitempty" toml:"CPUs,omitempty"`
405	Memory int64             `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"`
406	Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
407}
408
409// GraphDriver contains information about the GraphDriver used by the
410// container.
411type GraphDriver struct {
412	Name string            `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
413	Data map[string]string `json:"Data,omitempty" yaml:"Data,omitempty" toml:"Data,omitempty"`
414}
415
416// HealthConfig holds configuration settings for the HEALTHCHECK feature
417//
418// It has been added in the version 1.24 of the Docker API, available since
419// Docker 1.12.
420type HealthConfig struct {
421	// Test is the test to perform to check that the container is healthy.
422	// An empty slice means to inherit the default.
423	// The options are:
424	// {} : inherit healthcheck
425	// {"NONE"} : disable healthcheck
426	// {"CMD", args...} : exec arguments directly
427	// {"CMD-SHELL", command} : run command with system's default shell
428	Test []string `json:"Test,omitempty" yaml:"Test,omitempty" toml:"Test,omitempty"`
429
430	// Zero means to inherit. Durations are expressed as integer nanoseconds.
431	Interval    time.Duration `json:"Interval,omitempty" yaml:"Interval,omitempty" toml:"Interval,omitempty"`          // Interval is the time to wait between checks.
432	Timeout     time.Duration `json:"Timeout,omitempty" yaml:"Timeout,omitempty" toml:"Timeout,omitempty"`             // Timeout is the time to wait before considering the check to have hung.
433	StartPeriod time.Duration `json:"StartPeriod,omitempty" yaml:"StartPeriod,omitempty" toml:"StartPeriod,omitempty"` // The start period for the container to initialize before the retries starts to count down.
434
435	// Retries is the number of consecutive failures needed to consider a container as unhealthy.
436	// Zero means inherit.
437	Retries int `json:"Retries,omitempty" yaml:"Retries,omitempty" toml:"Retries,omitempty"`
438}
439
440// Container is the type encompasing everything about a container - its config,
441// hostconfig, etc.
442type Container struct {
443	ID string `json:"Id" yaml:"Id" toml:"Id"`
444
445	Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
446
447	Path string   `json:"Path,omitempty" yaml:"Path,omitempty" toml:"Path,omitempty"`
448	Args []string `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"`
449
450	Config *Config `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
451	State  State   `json:"State,omitempty" yaml:"State,omitempty" toml:"State,omitempty"`
452	Image  string  `json:"Image,omitempty" yaml:"Image,omitempty" toml:"Image,omitempty"`
453
454	Node *SwarmNode `json:"Node,omitempty" yaml:"Node,omitempty" toml:"Node,omitempty"`
455
456	NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty" toml:"NetworkSettings,omitempty"`
457
458	SysInitPath    string  `json:"SysInitPath,omitempty" yaml:"SysInitPath,omitempty" toml:"SysInitPath,omitempty"`
459	ResolvConfPath string  `json:"ResolvConfPath,omitempty" yaml:"ResolvConfPath,omitempty" toml:"ResolvConfPath,omitempty"`
460	HostnamePath   string  `json:"HostnamePath,omitempty" yaml:"HostnamePath,omitempty" toml:"HostnamePath,omitempty"`
461	HostsPath      string  `json:"HostsPath,omitempty" yaml:"HostsPath,omitempty" toml:"HostsPath,omitempty"`
462	LogPath        string  `json:"LogPath,omitempty" yaml:"LogPath,omitempty" toml:"LogPath,omitempty"`
463	Name           string  `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
464	Driver         string  `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"`
465	Mounts         []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"`
466
467	Volumes     map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty" toml:"Volumes,omitempty"`
468	VolumesRW   map[string]bool   `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty" toml:"VolumesRW,omitempty"`
469	HostConfig  *HostConfig       `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty" toml:"HostConfig,omitempty"`
470	ExecIDs     []string          `json:"ExecIDs,omitempty" yaml:"ExecIDs,omitempty" toml:"ExecIDs,omitempty"`
471	GraphDriver *GraphDriver      `json:"GraphDriver,omitempty" yaml:"GraphDriver,omitempty" toml:"GraphDriver,omitempty"`
472
473	RestartCount int `json:"RestartCount,omitempty" yaml:"RestartCount,omitempty" toml:"RestartCount,omitempty"`
474
475	AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty" toml:"AppArmorProfile,omitempty"`
476}
477
478// UpdateContainerOptions specify parameters to the UpdateContainer function.
479//
480// See https://goo.gl/Y6fXUy for more details.
481type UpdateContainerOptions struct {
482	BlkioWeight        int           `json:"BlkioWeight"`
483	CPUShares          int           `json:"CpuShares"`
484	CPUPeriod          int           `json:"CpuPeriod"`
485	CPURealtimePeriod  int64         `json:"CpuRealtimePeriod"`
486	CPURealtimeRuntime int64         `json:"CpuRealtimeRuntime"`
487	CPUQuota           int           `json:"CpuQuota"`
488	CpusetCpus         string        `json:"CpusetCpus"`
489	CpusetMems         string        `json:"CpusetMems"`
490	Memory             int           `json:"Memory"`
491	MemorySwap         int           `json:"MemorySwap"`
492	MemoryReservation  int           `json:"MemoryReservation"`
493	KernelMemory       int           `json:"KernelMemory"`
494	RestartPolicy      RestartPolicy `json:"RestartPolicy,omitempty"`
495	Context            context.Context
496}
497
498// UpdateContainer updates the container at ID with the options
499//
500// See https://goo.gl/Y6fXUy for more details.
501func (c *Client) UpdateContainer(id string, opts UpdateContainerOptions) error {
502	resp, err := c.do("POST", fmt.Sprintf("/containers/"+id+"/update"), doOptions{
503		data:      opts,
504		forceJSON: true,
505		context:   opts.Context,
506	})
507	if err != nil {
508		return err
509	}
510	defer resp.Body.Close()
511	return nil
512}
513
514// RenameContainerOptions specify parameters to the RenameContainer function.
515//
516// See https://goo.gl/46inai for more details.
517type RenameContainerOptions struct {
518	// ID of container to rename
519	ID string `qs:"-"`
520
521	// New name
522	Name    string `json:"name,omitempty" yaml:"name,omitempty"`
523	Context context.Context
524}
525
526// RenameContainer updates and existing containers name
527//
528// See https://goo.gl/46inai for more details.
529func (c *Client) RenameContainer(opts RenameContainerOptions) error {
530	resp, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{
531		context: opts.Context,
532	})
533	if err != nil {
534		return err
535	}
536	resp.Body.Close()
537	return nil
538}
539
540// InspectContainer returns information about a container by its ID.
541//
542// See https://goo.gl/FaI5JT for more details.
543func (c *Client) InspectContainer(id string) (*Container, error) {
544	return c.inspectContainer(id, doOptions{})
545}
546
547// InspectContainerWithContext returns information about a container by its ID.
548// The context object can be used to cancel the inspect request.
549//
550// See https://goo.gl/FaI5JT for more details.
551func (c *Client) InspectContainerWithContext(id string, ctx context.Context) (*Container, error) {
552	return c.inspectContainer(id, doOptions{context: ctx})
553}
554
555func (c *Client) inspectContainer(id string, opts doOptions) (*Container, error) {
556	path := "/containers/" + id + "/json"
557	resp, err := c.do("GET", path, opts)
558	if err != nil {
559		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
560			return nil, &NoSuchContainer{ID: id}
561		}
562		return nil, err
563	}
564	defer resp.Body.Close()
565	var container Container
566	if err := json.NewDecoder(resp.Body).Decode(&container); err != nil {
567		return nil, err
568	}
569	return &container, nil
570}
571
572// ContainerChanges returns changes in the filesystem of the given container.
573//
574// See https://goo.gl/15KKzh for more details.
575func (c *Client) ContainerChanges(id string) ([]Change, error) {
576	path := "/containers/" + id + "/changes"
577	resp, err := c.do("GET", path, doOptions{})
578	if err != nil {
579		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
580			return nil, &NoSuchContainer{ID: id}
581		}
582		return nil, err
583	}
584	defer resp.Body.Close()
585	var changes []Change
586	if err := json.NewDecoder(resp.Body).Decode(&changes); err != nil {
587		return nil, err
588	}
589	return changes, nil
590}
591
592// CreateContainerOptions specify parameters to the CreateContainer function.
593//
594// See https://goo.gl/tyzwVM for more details.
595type CreateContainerOptions struct {
596	Name             string
597	Config           *Config           `qs:"-"`
598	HostConfig       *HostConfig       `qs:"-"`
599	NetworkingConfig *NetworkingConfig `qs:"-"`
600	Context          context.Context
601}
602
603// CreateContainer creates a new container, returning the container instance,
604// or an error in case of failure.
605//
606// The returned container instance contains only the container ID. To get more
607// details about the container after creating it, use InspectContainer.
608//
609// See https://goo.gl/tyzwVM for more details.
610func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
611	path := "/containers/create?" + queryString(opts)
612	resp, err := c.do(
613		"POST",
614		path,
615		doOptions{
616			data: struct {
617				*Config
618				HostConfig       *HostConfig       `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty" toml:"HostConfig,omitempty"`
619				NetworkingConfig *NetworkingConfig `json:"NetworkingConfig,omitempty" yaml:"NetworkingConfig,omitempty" toml:"NetworkingConfig,omitempty"`
620			}{
621				opts.Config,
622				opts.HostConfig,
623				opts.NetworkingConfig,
624			},
625			context: opts.Context,
626		},
627	)
628
629	if e, ok := err.(*Error); ok {
630		if e.Status == http.StatusNotFound {
631			return nil, ErrNoSuchImage
632		}
633		if e.Status == http.StatusConflict {
634			return nil, ErrContainerAlreadyExists
635		}
636		// Workaround for 17.09 bug returning 400 instead of 409.
637		// See https://github.com/moby/moby/issues/35021
638		if e.Status == http.StatusBadRequest && strings.Contains(e.Message, "Conflict.") {
639			return nil, ErrContainerAlreadyExists
640		}
641	}
642
643	if err != nil {
644		return nil, err
645	}
646	defer resp.Body.Close()
647	var container Container
648	if err := json.NewDecoder(resp.Body).Decode(&container); err != nil {
649		return nil, err
650	}
651
652	container.Name = opts.Name
653
654	return &container, nil
655}
656
657// KeyValuePair is a type for generic key/value pairs as used in the Lxc
658// configuration
659type KeyValuePair struct {
660	Key   string `json:"Key,omitempty" yaml:"Key,omitempty" toml:"Key,omitempty"`
661	Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
662}
663
664// RestartPolicy represents the policy for automatically restarting a container.
665//
666// Possible values are:
667//
668//   - always: the docker daemon will always restart the container
669//   - on-failure: the docker daemon will restart the container on failures, at
670//                 most MaximumRetryCount times
671//   - unless-stopped: the docker daemon will always restart the container except
672//                 when user has manually stopped the container
673//   - no: the docker daemon will not restart the container automatically
674type RestartPolicy struct {
675	Name              string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
676	MaximumRetryCount int    `json:"MaximumRetryCount,omitempty" yaml:"MaximumRetryCount,omitempty" toml:"MaximumRetryCount,omitempty"`
677}
678
679// AlwaysRestart returns a restart policy that tells the Docker daemon to
680// always restart the container.
681func AlwaysRestart() RestartPolicy {
682	return RestartPolicy{Name: "always"}
683}
684
685// RestartOnFailure returns a restart policy that tells the Docker daemon to
686// restart the container on failures, trying at most maxRetry times.
687func RestartOnFailure(maxRetry int) RestartPolicy {
688	return RestartPolicy{Name: "on-failure", MaximumRetryCount: maxRetry}
689}
690
691// RestartUnlessStopped returns a restart policy that tells the Docker daemon to
692// always restart the container except when user has manually stopped the container.
693func RestartUnlessStopped() RestartPolicy {
694	return RestartPolicy{Name: "unless-stopped"}
695}
696
697// NeverRestart returns a restart policy that tells the Docker daemon to never
698// restart the container on failures.
699func NeverRestart() RestartPolicy {
700	return RestartPolicy{Name: "no"}
701}
702
703// Device represents a device mapping between the Docker host and the
704// container.
705type Device struct {
706	PathOnHost        string `json:"PathOnHost,omitempty" yaml:"PathOnHost,omitempty" toml:"PathOnHost,omitempty"`
707	PathInContainer   string `json:"PathInContainer,omitempty" yaml:"PathInContainer,omitempty" toml:"PathInContainer,omitempty"`
708	CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty" toml:"CgroupPermissions,omitempty"`
709}
710
711// BlockWeight represents a relative device weight for an individual device inside
712// of a container
713type BlockWeight struct {
714	Path   string `json:"Path,omitempty"`
715	Weight string `json:"Weight,omitempty"`
716}
717
718// BlockLimit represents a read/write limit in IOPS or Bandwidth for a device
719// inside of a container
720type BlockLimit struct {
721	Path string `json:"Path,omitempty"`
722	Rate int64  `json:"Rate,omitempty"`
723}
724
725// HostConfig contains the container options related to starting a container on
726// a given host
727type HostConfig struct {
728	Binds                []string               `json:"Binds,omitempty" yaml:"Binds,omitempty" toml:"Binds,omitempty"`
729	CapAdd               []string               `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty" toml:"CapAdd,omitempty"`
730	CapDrop              []string               `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty" toml:"CapDrop,omitempty"`
731	GroupAdd             []string               `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty" toml:"GroupAdd,omitempty"`
732	ContainerIDFile      string                 `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty" toml:"ContainerIDFile,omitempty"`
733	LxcConf              []KeyValuePair         `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty" toml:"LxcConf,omitempty"`
734	PortBindings         map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty" toml:"PortBindings,omitempty"`
735	Links                []string               `json:"Links,omitempty" yaml:"Links,omitempty" toml:"Links,omitempty"`
736	DNS                  []string               `json:"Dns,omitempty" yaml:"Dns,omitempty" toml:"Dns,omitempty"` // For Docker API v1.10 and above only
737	DNSOptions           []string               `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty" toml:"DnsOptions,omitempty"`
738	DNSSearch            []string               `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty" toml:"DnsSearch,omitempty"`
739	ExtraHosts           []string               `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty" toml:"ExtraHosts,omitempty"`
740	VolumesFrom          []string               `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty" toml:"VolumesFrom,omitempty"`
741	UsernsMode           string                 `json:"UsernsMode,omitempty" yaml:"UsernsMode,omitempty" toml:"UsernsMode,omitempty"`
742	NetworkMode          string                 `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty" toml:"NetworkMode,omitempty"`
743	IpcMode              string                 `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty" toml:"IpcMode,omitempty"`
744	PidMode              string                 `json:"PidMode,omitempty" yaml:"PidMode,omitempty" toml:"PidMode,omitempty"`
745	UTSMode              string                 `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty" toml:"UTSMode,omitempty"`
746	RestartPolicy        RestartPolicy          `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty" toml:"RestartPolicy,omitempty"`
747	Devices              []Device               `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"`
748	DeviceCgroupRules    []string               `json:"DeviceCgroupRules,omitempty" yaml:"DeviceCgroupRules,omitempty" toml:"DeviceCgroupRules,omitempty"`
749	LogConfig            LogConfig              `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty" toml:"LogConfig,omitempty"`
750	SecurityOpt          []string               `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty" toml:"SecurityOpt,omitempty"`
751	Cgroup               string                 `json:"Cgroup,omitempty" yaml:"Cgroup,omitempty" toml:"Cgroup,omitempty"`
752	CgroupParent         string                 `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty" toml:"CgroupParent,omitempty"`
753	Memory               int64                  `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"`
754	MemoryReservation    int64                  `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty" toml:"MemoryReservation,omitempty"`
755	KernelMemory         int64                  `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty" toml:"KernelMemory,omitempty"`
756	MemorySwap           int64                  `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty" toml:"MemorySwap,omitempty"`
757	MemorySwappiness     int64                  `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty" toml:"MemorySwappiness,omitempty"`
758	CPUShares            int64                  `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty" toml:"CpuShares,omitempty"`
759	CPUSet               string                 `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty" toml:"Cpuset,omitempty"`
760	CPUSetCPUs           string                 `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty" toml:"CpusetCpus,omitempty"`
761	CPUSetMEMs           string                 `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty" toml:"CpusetMems,omitempty"`
762	CPUQuota             int64                  `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty" toml:"CpuQuota,omitempty"`
763	CPUPeriod            int64                  `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty" toml:"CpuPeriod,omitempty"`
764	CPURealtimePeriod    int64                  `json:"CpuRealtimePeriod,omitempty" yaml:"CpuRealtimePeriod,omitempty" toml:"CpuRealtimePeriod,omitempty"`
765	CPURealtimeRuntime   int64                  `json:"CpuRealtimeRuntime,omitempty" yaml:"CpuRealtimeRuntime,omitempty" toml:"CpuRealtimeRuntime,omitempty"`
766	BlkioWeight          int64                  `json:"BlkioWeight,omitempty" yaml:"BlkioWeight,omitempty" toml:"BlkioWeight,omitempty"`
767	BlkioWeightDevice    []BlockWeight          `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice,omitempty" toml:"BlkioWeightDevice,omitempty"`
768	BlkioDeviceReadBps   []BlockLimit           `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps,omitempty" toml:"BlkioDeviceReadBps,omitempty"`
769	BlkioDeviceReadIOps  []BlockLimit           `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps,omitempty" toml:"BlkioDeviceReadIOps,omitempty"`
770	BlkioDeviceWriteBps  []BlockLimit           `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps,omitempty" toml:"BlkioDeviceWriteBps,omitempty"`
771	BlkioDeviceWriteIOps []BlockLimit           `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps,omitempty" toml:"BlkioDeviceWriteIOps,omitempty"`
772	Ulimits              []ULimit               `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty" toml:"Ulimits,omitempty"`
773	VolumeDriver         string                 `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty" toml:"VolumeDriver,omitempty"`
774	OomScoreAdj          int                    `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty" toml:"OomScoreAdj,omitempty"`
775	PidsLimit            int64                  `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty" toml:"PidsLimit,omitempty"`
776	ShmSize              int64                  `json:"ShmSize,omitempty" yaml:"ShmSize,omitempty" toml:"ShmSize,omitempty"`
777	Tmpfs                map[string]string      `json:"Tmpfs,omitempty" yaml:"Tmpfs,omitempty" toml:"Tmpfs,omitempty"`
778	Privileged           bool                   `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
779	PublishAllPorts      bool                   `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty" toml:"PublishAllPorts,omitempty"`
780	ReadonlyRootfs       bool                   `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty" toml:"ReadonlyRootfs,omitempty"`
781	OOMKillDisable       bool                   `json:"OomKillDisable,omitempty" yaml:"OomKillDisable,omitempty" toml:"OomKillDisable,omitempty"`
782	AutoRemove           bool                   `json:"AutoRemove,omitempty" yaml:"AutoRemove,omitempty" toml:"AutoRemove,omitempty"`
783	StorageOpt           map[string]string      `json:"StorageOpt,omitempty" yaml:"StorageOpt,omitempty" toml:"StorageOpt,omitempty"`
784	Sysctls              map[string]string      `json:"Sysctls,omitempty" yaml:"Sysctls,omitempty" toml:"Sysctls,omitempty"`
785	CPUCount             int64                  `json:"CpuCount,omitempty" yaml:"CpuCount,omitempty"`
786	CPUPercent           int64                  `json:"CpuPercent,omitempty" yaml:"CpuPercent,omitempty"`
787	IOMaximumBandwidth   int64                  `json:"IOMaximumBandwidth,omitempty" yaml:"IOMaximumBandwidth,omitempty"`
788	IOMaximumIOps        int64                  `json:"IOMaximumIOps,omitempty" yaml:"IOMaximumIOps,omitempty"`
789	Mounts               []HostMount            `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"`
790	Init                 bool                   `json:",omitempty" yaml:",omitempty"`
791	Runtime              string                 `json:"Runtime,omitempty" yaml:"Runtime,omitempty" toml:"Runtime,omitempty"`
792}
793
794// NetworkingConfig represents the container's networking configuration for each of its interfaces
795// Carries the networking configs specified in the `docker run` and `docker network connect` commands
796type NetworkingConfig struct {
797	EndpointsConfig map[string]*EndpointConfig `json:"EndpointsConfig" yaml:"EndpointsConfig" toml:"EndpointsConfig"` // Endpoint configs for each connecting network
798}
799
800// StartContainer starts a container, returning an error in case of failure.
801//
802// Passing the HostConfig to this method has been deprecated in Docker API 1.22
803// (Docker Engine 1.10.x) and totally removed in Docker API 1.24 (Docker Engine
804// 1.12.x). The client will ignore the parameter when communicating with Docker
805// API 1.24 or greater.
806//
807// See https://goo.gl/fbOSZy for more details.
808func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
809	return c.startContainer(id, hostConfig, doOptions{})
810}
811
812// StartContainerWithContext starts a container, returning an error in case of
813// failure. The context can be used to cancel the outstanding start container
814// request.
815//
816// Passing the HostConfig to this method has been deprecated in Docker API 1.22
817// (Docker Engine 1.10.x) and totally removed in Docker API 1.24 (Docker Engine
818// 1.12.x). The client will ignore the parameter when communicating with Docker
819// API 1.24 or greater.
820//
821// See https://goo.gl/fbOSZy for more details.
822func (c *Client) StartContainerWithContext(id string, hostConfig *HostConfig, ctx context.Context) error {
823	return c.startContainer(id, hostConfig, doOptions{context: ctx})
824}
825
826func (c *Client) startContainer(id string, hostConfig *HostConfig, opts doOptions) error {
827	path := "/containers/" + id + "/start"
828	if c.serverAPIVersion == nil {
829		c.checkAPIVersion()
830	}
831	if c.serverAPIVersion != nil && c.serverAPIVersion.LessThan(apiVersion124) {
832		opts.data = hostConfig
833		opts.forceJSON = true
834	}
835	resp, err := c.do("POST", path, opts)
836	if err != nil {
837		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
838			return &NoSuchContainer{ID: id, Err: err}
839		}
840		return err
841	}
842	defer resp.Body.Close()
843	if resp.StatusCode == http.StatusNotModified {
844		return &ContainerAlreadyRunning{ID: id}
845	}
846	return nil
847}
848
849// StopContainer stops a container, killing it after the given timeout (in
850// seconds).
851//
852// See https://goo.gl/R9dZcV for more details.
853func (c *Client) StopContainer(id string, timeout uint) error {
854	return c.stopContainer(id, timeout, doOptions{})
855}
856
857// StopContainerWithContext stops a container, killing it after the given
858// timeout (in seconds). The context can be used to cancel the stop
859// container request.
860//
861// See https://goo.gl/R9dZcV for more details.
862func (c *Client) StopContainerWithContext(id string, timeout uint, ctx context.Context) error {
863	return c.stopContainer(id, timeout, doOptions{context: ctx})
864}
865
866func (c *Client) stopContainer(id string, timeout uint, opts doOptions) error {
867	path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
868	resp, err := c.do("POST", path, opts)
869	if err != nil {
870		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
871			return &NoSuchContainer{ID: id}
872		}
873		return err
874	}
875	defer resp.Body.Close()
876	if resp.StatusCode == http.StatusNotModified {
877		return &ContainerNotRunning{ID: id}
878	}
879	return nil
880}
881
882// RestartContainer stops a container, killing it after the given timeout (in
883// seconds), during the stop process.
884//
885// See https://goo.gl/MrAKQ5 for more details.
886func (c *Client) RestartContainer(id string, timeout uint) error {
887	path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
888	resp, err := c.do("POST", path, doOptions{})
889	if err != nil {
890		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
891			return &NoSuchContainer{ID: id}
892		}
893		return err
894	}
895	resp.Body.Close()
896	return nil
897}
898
899// PauseContainer pauses the given container.
900//
901// See https://goo.gl/D1Yaii for more details.
902func (c *Client) PauseContainer(id string) error {
903	path := fmt.Sprintf("/containers/%s/pause", id)
904	resp, err := c.do("POST", path, doOptions{})
905	if err != nil {
906		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
907			return &NoSuchContainer{ID: id}
908		}
909		return err
910	}
911	resp.Body.Close()
912	return nil
913}
914
915// UnpauseContainer unpauses the given container.
916//
917// See https://goo.gl/sZ2faO for more details.
918func (c *Client) UnpauseContainer(id string) error {
919	path := fmt.Sprintf("/containers/%s/unpause", id)
920	resp, err := c.do("POST", path, doOptions{})
921	if err != nil {
922		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
923			return &NoSuchContainer{ID: id}
924		}
925		return err
926	}
927	resp.Body.Close()
928	return nil
929}
930
931// TopResult represents the list of processes running in a container, as
932// returned by /containers/<id>/top.
933//
934// See https://goo.gl/FLwpPl for more details.
935type TopResult struct {
936	Titles    []string
937	Processes [][]string
938}
939
940// TopContainer returns processes running inside a container
941//
942// See https://goo.gl/FLwpPl for more details.
943func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
944	var args string
945	var result TopResult
946	if psArgs != "" {
947		args = fmt.Sprintf("?ps_args=%s", psArgs)
948	}
949	path := fmt.Sprintf("/containers/%s/top%s", id, args)
950	resp, err := c.do("GET", path, doOptions{})
951	if err != nil {
952		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
953			return result, &NoSuchContainer{ID: id}
954		}
955		return result, err
956	}
957	defer resp.Body.Close()
958	err = json.NewDecoder(resp.Body).Decode(&result)
959	return result, err
960}
961
962// Stats represents container statistics, returned by /containers/<id>/stats.
963//
964// See https://goo.gl/Dk3Xio for more details.
965type Stats struct {
966	Read      time.Time `json:"read,omitempty" yaml:"read,omitempty" toml:"read,omitempty"`
967	PreRead   time.Time `json:"preread,omitempty" yaml:"preread,omitempty" toml:"preread,omitempty"`
968	NumProcs  uint32    `json:"num_procs" yaml:"num_procs" toml:"num_procs"`
969	PidsStats struct {
970		Current uint64 `json:"current,omitempty" yaml:"current,omitempty"`
971	} `json:"pids_stats,omitempty" yaml:"pids_stats,omitempty" toml:"pids_stats,omitempty"`
972	Network     NetworkStats            `json:"network,omitempty" yaml:"network,omitempty" toml:"network,omitempty"`
973	Networks    map[string]NetworkStats `json:"networks,omitempty" yaml:"networks,omitempty" toml:"networks,omitempty"`
974	MemoryStats struct {
975		Stats struct {
976			TotalPgmafault          uint64 `json:"total_pgmafault,omitempty" yaml:"total_pgmafault,omitempty" toml:"total_pgmafault,omitempty"`
977			Cache                   uint64 `json:"cache,omitempty" yaml:"cache,omitempty" toml:"cache,omitempty"`
978			MappedFile              uint64 `json:"mapped_file,omitempty" yaml:"mapped_file,omitempty" toml:"mapped_file,omitempty"`
979			TotalInactiveFile       uint64 `json:"total_inactive_file,omitempty" yaml:"total_inactive_file,omitempty" toml:"total_inactive_file,omitempty"`
980			Pgpgout                 uint64 `json:"pgpgout,omitempty" yaml:"pgpgout,omitempty" toml:"pgpgout,omitempty"`
981			Rss                     uint64 `json:"rss,omitempty" yaml:"rss,omitempty" toml:"rss,omitempty"`
982			TotalMappedFile         uint64 `json:"total_mapped_file,omitempty" yaml:"total_mapped_file,omitempty" toml:"total_mapped_file,omitempty"`
983			Writeback               uint64 `json:"writeback,omitempty" yaml:"writeback,omitempty" toml:"writeback,omitempty"`
984			Unevictable             uint64 `json:"unevictable,omitempty" yaml:"unevictable,omitempty" toml:"unevictable,omitempty"`
985			Pgpgin                  uint64 `json:"pgpgin,omitempty" yaml:"pgpgin,omitempty" toml:"pgpgin,omitempty"`
986			TotalUnevictable        uint64 `json:"total_unevictable,omitempty" yaml:"total_unevictable,omitempty" toml:"total_unevictable,omitempty"`
987			Pgmajfault              uint64 `json:"pgmajfault,omitempty" yaml:"pgmajfault,omitempty" toml:"pgmajfault,omitempty"`
988			TotalRss                uint64 `json:"total_rss,omitempty" yaml:"total_rss,omitempty" toml:"total_rss,omitempty"`
989			TotalRssHuge            uint64 `json:"total_rss_huge,omitempty" yaml:"total_rss_huge,omitempty" toml:"total_rss_huge,omitempty"`
990			TotalWriteback          uint64 `json:"total_writeback,omitempty" yaml:"total_writeback,omitempty" toml:"total_writeback,omitempty"`
991			TotalInactiveAnon       uint64 `json:"total_inactive_anon,omitempty" yaml:"total_inactive_anon,omitempty" toml:"total_inactive_anon,omitempty"`
992			RssHuge                 uint64 `json:"rss_huge,omitempty" yaml:"rss_huge,omitempty" toml:"rss_huge,omitempty"`
993			HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit,omitempty" yaml:"hierarchical_memory_limit,omitempty" toml:"hierarchical_memory_limit,omitempty"`
994			TotalPgfault            uint64 `json:"total_pgfault,omitempty" yaml:"total_pgfault,omitempty" toml:"total_pgfault,omitempty"`
995			TotalActiveFile         uint64 `json:"total_active_file,omitempty" yaml:"total_active_file,omitempty" toml:"total_active_file,omitempty"`
996			ActiveAnon              uint64 `json:"active_anon,omitempty" yaml:"active_anon,omitempty" toml:"active_anon,omitempty"`
997			TotalActiveAnon         uint64 `json:"total_active_anon,omitempty" yaml:"total_active_anon,omitempty" toml:"total_active_anon,omitempty"`
998			TotalPgpgout            uint64 `json:"total_pgpgout,omitempty" yaml:"total_pgpgout,omitempty" toml:"total_pgpgout,omitempty"`
999			TotalCache              uint64 `json:"total_cache,omitempty" yaml:"total_cache,omitempty" toml:"total_cache,omitempty"`
1000			InactiveAnon            uint64 `json:"inactive_anon,omitempty" yaml:"inactive_anon,omitempty" toml:"inactive_anon,omitempty"`
1001			ActiveFile              uint64 `json:"active_file,omitempty" yaml:"active_file,omitempty" toml:"active_file,omitempty"`
1002			Pgfault                 uint64 `json:"pgfault,omitempty" yaml:"pgfault,omitempty" toml:"pgfault,omitempty"`
1003			InactiveFile            uint64 `json:"inactive_file,omitempty" yaml:"inactive_file,omitempty" toml:"inactive_file,omitempty"`
1004			TotalPgpgin             uint64 `json:"total_pgpgin,omitempty" yaml:"total_pgpgin,omitempty" toml:"total_pgpgin,omitempty"`
1005			HierarchicalMemswLimit  uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty" toml:"hierarchical_memsw_limit,omitempty"`
1006			Swap                    uint64 `json:"swap,omitempty" yaml:"swap,omitempty" toml:"swap,omitempty"`
1007		} `json:"stats,omitempty" yaml:"stats,omitempty" toml:"stats,omitempty"`
1008		MaxUsage          uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty" toml:"max_usage,omitempty"`
1009		Usage             uint64 `json:"usage,omitempty" yaml:"usage,omitempty" toml:"usage,omitempty"`
1010		Failcnt           uint64 `json:"failcnt,omitempty" yaml:"failcnt,omitempty" toml:"failcnt,omitempty"`
1011		Limit             uint64 `json:"limit,omitempty" yaml:"limit,omitempty" toml:"limit,omitempty"`
1012		Commit            uint64 `json:"commitbytes,omitempty" yaml:"commitbytes,omitempty" toml:"privateworkingset,omitempty"`
1013		CommitPeak        uint64 `json:"commitpeakbytes,omitempty" yaml:"commitpeakbytes,omitempty" toml:"commitpeakbytes,omitempty"`
1014		PrivateWorkingSet uint64 `json:"privateworkingset,omitempty" yaml:"privateworkingset,omitempty" toml:"privateworkingset,omitempty"`
1015	} `json:"memory_stats,omitempty" yaml:"memory_stats,omitempty" toml:"memory_stats,omitempty"`
1016	BlkioStats struct {
1017		IOServiceBytesRecursive []BlkioStatsEntry `json:"io_service_bytes_recursive,omitempty" yaml:"io_service_bytes_recursive,omitempty" toml:"io_service_bytes_recursive,omitempty"`
1018		IOServicedRecursive     []BlkioStatsEntry `json:"io_serviced_recursive,omitempty" yaml:"io_serviced_recursive,omitempty" toml:"io_serviced_recursive,omitempty"`
1019		IOQueueRecursive        []BlkioStatsEntry `json:"io_queue_recursive,omitempty" yaml:"io_queue_recursive,omitempty" toml:"io_queue_recursive,omitempty"`
1020		IOServiceTimeRecursive  []BlkioStatsEntry `json:"io_service_time_recursive,omitempty" yaml:"io_service_time_recursive,omitempty" toml:"io_service_time_recursive,omitempty"`
1021		IOWaitTimeRecursive     []BlkioStatsEntry `json:"io_wait_time_recursive,omitempty" yaml:"io_wait_time_recursive,omitempty" toml:"io_wait_time_recursive,omitempty"`
1022		IOMergedRecursive       []BlkioStatsEntry `json:"io_merged_recursive,omitempty" yaml:"io_merged_recursive,omitempty" toml:"io_merged_recursive,omitempty"`
1023		IOTimeRecursive         []BlkioStatsEntry `json:"io_time_recursive,omitempty" yaml:"io_time_recursive,omitempty" toml:"io_time_recursive,omitempty"`
1024		SectorsRecursive        []BlkioStatsEntry `json:"sectors_recursive,omitempty" yaml:"sectors_recursive,omitempty" toml:"sectors_recursive,omitempty"`
1025	} `json:"blkio_stats,omitempty" yaml:"blkio_stats,omitempty" toml:"blkio_stats,omitempty"`
1026	CPUStats     CPUStats `json:"cpu_stats,omitempty" yaml:"cpu_stats,omitempty" toml:"cpu_stats,omitempty"`
1027	PreCPUStats  CPUStats `json:"precpu_stats,omitempty"`
1028	StorageStats struct {
1029		ReadCountNormalized  uint64 `json:"read_count_normalized,omitempty" yaml:"read_count_normalized,omitempty" toml:"read_count_normalized,omitempty"`
1030		ReadSizeBytes        uint64 `json:"read_size_bytes,omitempty" yaml:"read_size_bytes,omitempty" toml:"read_size_bytes,omitempty"`
1031		WriteCountNormalized uint64 `json:"write_count_normalized,omitempty" yaml:"write_count_normalized,omitempty" toml:"write_count_normalized,omitempty"`
1032		WriteSizeBytes       uint64 `json:"write_size_bytes,omitempty" yaml:"write_size_bytes,omitempty" toml:"write_size_bytes,omitempty"`
1033	} `json:"storage_stats,omitempty" yaml:"storage_stats,omitempty" toml:"storage_stats,omitempty"`
1034}
1035
1036// NetworkStats is a stats entry for network stats
1037type NetworkStats struct {
1038	RxDropped uint64 `json:"rx_dropped,omitempty" yaml:"rx_dropped,omitempty" toml:"rx_dropped,omitempty"`
1039	RxBytes   uint64 `json:"rx_bytes,omitempty" yaml:"rx_bytes,omitempty" toml:"rx_bytes,omitempty"`
1040	RxErrors  uint64 `json:"rx_errors,omitempty" yaml:"rx_errors,omitempty" toml:"rx_errors,omitempty"`
1041	TxPackets uint64 `json:"tx_packets,omitempty" yaml:"tx_packets,omitempty" toml:"tx_packets,omitempty"`
1042	TxDropped uint64 `json:"tx_dropped,omitempty" yaml:"tx_dropped,omitempty" toml:"tx_dropped,omitempty"`
1043	RxPackets uint64 `json:"rx_packets,omitempty" yaml:"rx_packets,omitempty" toml:"rx_packets,omitempty"`
1044	TxErrors  uint64 `json:"tx_errors,omitempty" yaml:"tx_errors,omitempty" toml:"tx_errors,omitempty"`
1045	TxBytes   uint64 `json:"tx_bytes,omitempty" yaml:"tx_bytes,omitempty" toml:"tx_bytes,omitempty"`
1046}
1047
1048// CPUStats is a stats entry for cpu stats
1049type CPUStats struct {
1050	CPUUsage struct {
1051		PercpuUsage       []uint64 `json:"percpu_usage,omitempty" yaml:"percpu_usage,omitempty" toml:"percpu_usage,omitempty"`
1052		UsageInUsermode   uint64   `json:"usage_in_usermode,omitempty" yaml:"usage_in_usermode,omitempty" toml:"usage_in_usermode,omitempty"`
1053		TotalUsage        uint64   `json:"total_usage,omitempty" yaml:"total_usage,omitempty" toml:"total_usage,omitempty"`
1054		UsageInKernelmode uint64   `json:"usage_in_kernelmode,omitempty" yaml:"usage_in_kernelmode,omitempty" toml:"usage_in_kernelmode,omitempty"`
1055	} `json:"cpu_usage,omitempty" yaml:"cpu_usage,omitempty" toml:"cpu_usage,omitempty"`
1056	SystemCPUUsage uint64 `json:"system_cpu_usage,omitempty" yaml:"system_cpu_usage,omitempty" toml:"system_cpu_usage,omitempty"`
1057	OnlineCPUs     uint64 `json:"online_cpus,omitempty" yaml:"online_cpus,omitempty" toml:"online_cpus,omitempty"`
1058	ThrottlingData struct {
1059		Periods          uint64 `json:"periods,omitempty"`
1060		ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
1061		ThrottledTime    uint64 `json:"throttled_time,omitempty"`
1062	} `json:"throttling_data,omitempty" yaml:"throttling_data,omitempty" toml:"throttling_data,omitempty"`
1063}
1064
1065// BlkioStatsEntry is a stats entry for blkio_stats
1066type BlkioStatsEntry struct {
1067	Major uint64 `json:"major,omitempty" yaml:"major,omitempty" toml:"major,omitempty"`
1068	Minor uint64 `json:"minor,omitempty" yaml:"minor,omitempty" toml:"minor,omitempty"`
1069	Op    string `json:"op,omitempty" yaml:"op,omitempty" toml:"op,omitempty"`
1070	Value uint64 `json:"value,omitempty" yaml:"value,omitempty" toml:"value,omitempty"`
1071}
1072
1073// StatsOptions specify parameters to the Stats function.
1074//
1075// See https://goo.gl/Dk3Xio for more details.
1076type StatsOptions struct {
1077	ID     string
1078	Stats  chan<- *Stats
1079	Stream bool
1080	// A flag that enables stopping the stats operation
1081	Done <-chan bool
1082	// Initial connection timeout
1083	Timeout time.Duration
1084	// Timeout with no data is received, it's reset every time new data
1085	// arrives
1086	InactivityTimeout time.Duration `qs:"-"`
1087	Context           context.Context
1088}
1089
1090// Stats sends container statistics for the given container to the given channel.
1091//
1092// This function is blocking, similar to a streaming call for logs, and should be run
1093// on a separate goroutine from the caller. Note that this function will block until
1094// the given container is removed, not just exited. When finished, this function
1095// will close the given channel. Alternatively, function can be stopped by
1096// signaling on the Done channel.
1097//
1098// See https://goo.gl/Dk3Xio for more details.
1099func (c *Client) Stats(opts StatsOptions) (retErr error) {
1100	errC := make(chan error, 1)
1101	readCloser, writeCloser := io.Pipe()
1102
1103	defer func() {
1104		close(opts.Stats)
1105
1106		select {
1107		case err := <-errC:
1108			if err != nil && retErr == nil {
1109				retErr = err
1110			}
1111		default:
1112			// No errors
1113		}
1114
1115		if err := readCloser.Close(); err != nil && retErr == nil {
1116			retErr = err
1117		}
1118	}()
1119
1120	reqSent := make(chan struct{})
1121	go func() {
1122		err := c.stream("GET", fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{
1123			rawJSONStream:     true,
1124			useJSONDecoder:    true,
1125			stdout:            writeCloser,
1126			timeout:           opts.Timeout,
1127			inactivityTimeout: opts.InactivityTimeout,
1128			context:           opts.Context,
1129			reqSent:           reqSent,
1130		})
1131		if err != nil {
1132			dockerError, ok := err.(*Error)
1133			if ok {
1134				if dockerError.Status == http.StatusNotFound {
1135					err = &NoSuchContainer{ID: opts.ID}
1136				}
1137			}
1138		}
1139		if closeErr := writeCloser.Close(); closeErr != nil && err == nil {
1140			err = closeErr
1141		}
1142		errC <- err
1143		close(errC)
1144	}()
1145
1146	quit := make(chan struct{})
1147	defer close(quit)
1148	go func() {
1149		// block here waiting for the signal to stop function
1150		select {
1151		case <-opts.Done:
1152			readCloser.Close()
1153		case <-quit:
1154			return
1155		}
1156	}()
1157
1158	decoder := json.NewDecoder(readCloser)
1159	stats := new(Stats)
1160	<-reqSent
1161	for err := decoder.Decode(stats); err != io.EOF; err = decoder.Decode(stats) {
1162		if err != nil {
1163			return err
1164		}
1165		opts.Stats <- stats
1166		stats = new(Stats)
1167	}
1168	return nil
1169}
1170
1171// KillContainerOptions represents the set of options that can be used in a
1172// call to KillContainer.
1173//
1174// See https://goo.gl/JnTxXZ for more details.
1175type KillContainerOptions struct {
1176	// The ID of the container.
1177	ID string `qs:"-"`
1178
1179	// The signal to send to the container. When omitted, Docker server
1180	// will assume SIGKILL.
1181	Signal  Signal
1182	Context context.Context
1183}
1184
1185// KillContainer sends a signal to a container, returning an error in case of
1186// failure.
1187//
1188// See https://goo.gl/JnTxXZ for more details.
1189func (c *Client) KillContainer(opts KillContainerOptions) error {
1190	path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
1191	resp, err := c.do("POST", path, doOptions{context: opts.Context})
1192	if err != nil {
1193		e, ok := err.(*Error)
1194		if !ok {
1195			return err
1196		}
1197		switch e.Status {
1198		case http.StatusNotFound:
1199			return &NoSuchContainer{ID: opts.ID}
1200		case http.StatusConflict:
1201			return &ContainerNotRunning{ID: opts.ID}
1202		default:
1203			return err
1204		}
1205	}
1206	resp.Body.Close()
1207	return nil
1208}
1209
1210// RemoveContainerOptions encapsulates options to remove a container.
1211//
1212// See https://goo.gl/hL5IPC for more details.
1213type RemoveContainerOptions struct {
1214	// The ID of the container.
1215	ID string `qs:"-"`
1216
1217	// A flag that indicates whether Docker should remove the volumes
1218	// associated to the container.
1219	RemoveVolumes bool `qs:"v"`
1220
1221	// A flag that indicates whether Docker should remove the container
1222	// even if it is currently running.
1223	Force   bool
1224	Context context.Context
1225}
1226
1227// RemoveContainer removes a container, returning an error in case of failure.
1228//
1229// See https://goo.gl/hL5IPC for more details.
1230func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
1231	path := "/containers/" + opts.ID + "?" + queryString(opts)
1232	resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
1233	if err != nil {
1234		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
1235			return &NoSuchContainer{ID: opts.ID}
1236		}
1237		return err
1238	}
1239	resp.Body.Close()
1240	return nil
1241}
1242
1243// UploadToContainerOptions is the set of options that can be used when
1244// uploading an archive into a container.
1245//
1246// See https://goo.gl/g25o7u for more details.
1247type UploadToContainerOptions struct {
1248	InputStream          io.Reader `json:"-" qs:"-"`
1249	Path                 string    `qs:"path"`
1250	NoOverwriteDirNonDir bool      `qs:"noOverwriteDirNonDir"`
1251	Context              context.Context
1252}
1253
1254// UploadToContainer uploads a tar archive to be extracted to a path in the
1255// filesystem of the container.
1256//
1257// See https://goo.gl/g25o7u for more details.
1258func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) error {
1259	url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
1260
1261	return c.stream("PUT", url, streamOptions{
1262		in:      opts.InputStream,
1263		context: opts.Context,
1264	})
1265}
1266
1267// DownloadFromContainerOptions is the set of options that can be used when
1268// downloading resources from a container.
1269//
1270// See https://goo.gl/W49jxK for more details.
1271type DownloadFromContainerOptions struct {
1272	OutputStream      io.Writer     `json:"-" qs:"-"`
1273	Path              string        `qs:"path"`
1274	InactivityTimeout time.Duration `qs:"-"`
1275	Context           context.Context
1276}
1277
1278// DownloadFromContainer downloads a tar archive of files or folders in a container.
1279//
1280// See https://goo.gl/W49jxK for more details.
1281func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOptions) error {
1282	url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
1283
1284	return c.stream("GET", url, streamOptions{
1285		setRawTerminal:    true,
1286		stdout:            opts.OutputStream,
1287		inactivityTimeout: opts.InactivityTimeout,
1288		context:           opts.Context,
1289	})
1290}
1291
1292// CopyFromContainerOptions contains the set of options used for copying
1293// files from a container.
1294//
1295// Deprecated: Use DownloadFromContainerOptions and DownloadFromContainer instead.
1296type CopyFromContainerOptions struct {
1297	OutputStream io.Writer `json:"-"`
1298	Container    string    `json:"-"`
1299	Resource     string
1300	Context      context.Context `json:"-"`
1301}
1302
1303// CopyFromContainer copies files from a container.
1304//
1305// Deprecated: Use DownloadFromContainer and DownloadFromContainer instead.
1306func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
1307	if opts.Container == "" {
1308		return &NoSuchContainer{ID: opts.Container}
1309	}
1310	if c.serverAPIVersion == nil {
1311		c.checkAPIVersion()
1312	}
1313	if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion124) {
1314		return errors.New("go-dockerclient: CopyFromContainer is no longer available in Docker >= 1.12, use DownloadFromContainer instead")
1315	}
1316	url := fmt.Sprintf("/containers/%s/copy", opts.Container)
1317	resp, err := c.do("POST", url, doOptions{
1318		data:    opts,
1319		context: opts.Context,
1320	})
1321	if err != nil {
1322		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
1323			return &NoSuchContainer{ID: opts.Container}
1324		}
1325		return err
1326	}
1327	defer resp.Body.Close()
1328	_, err = io.Copy(opts.OutputStream, resp.Body)
1329	return err
1330}
1331
1332// WaitContainer blocks until the given container stops, return the exit code
1333// of the container status.
1334//
1335// See https://goo.gl/4AGweZ for more details.
1336func (c *Client) WaitContainer(id string) (int, error) {
1337	return c.waitContainer(id, doOptions{})
1338}
1339
1340// WaitContainerWithContext blocks until the given container stops, return the exit code
1341// of the container status. The context object can be used to cancel the
1342// inspect request.
1343//
1344// See https://goo.gl/4AGweZ for more details.
1345func (c *Client) WaitContainerWithContext(id string, ctx context.Context) (int, error) {
1346	return c.waitContainer(id, doOptions{context: ctx})
1347}
1348
1349func (c *Client) waitContainer(id string, opts doOptions) (int, error) {
1350	resp, err := c.do("POST", "/containers/"+id+"/wait", opts)
1351	if err != nil {
1352		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
1353			return 0, &NoSuchContainer{ID: id}
1354		}
1355		return 0, err
1356	}
1357	defer resp.Body.Close()
1358	var r struct{ StatusCode int }
1359	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
1360		return 0, err
1361	}
1362	return r.StatusCode, nil
1363}
1364
1365// CommitContainerOptions aggregates parameters to the CommitContainer method.
1366//
1367// See https://goo.gl/CzIguf for more details.
1368type CommitContainerOptions struct {
1369	Container  string
1370	Repository string `qs:"repo"`
1371	Tag        string
1372	Message    string `qs:"comment"`
1373	Author     string
1374	Changes    []string `qs:"changes"`
1375	Run        *Config  `qs:"-"`
1376	Context    context.Context
1377}
1378
1379// CommitContainer creates a new image from a container's changes.
1380//
1381// See https://goo.gl/CzIguf for more details.
1382func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
1383	path := "/commit?" + queryString(opts)
1384	resp, err := c.do("POST", path, doOptions{
1385		data:    opts.Run,
1386		context: opts.Context,
1387	})
1388	if err != nil {
1389		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
1390			return nil, &NoSuchContainer{ID: opts.Container}
1391		}
1392		return nil, err
1393	}
1394	defer resp.Body.Close()
1395	var image Image
1396	if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
1397		return nil, err
1398	}
1399	return &image, nil
1400}
1401
1402// AttachToContainerOptions is the set of options that can be used when
1403// attaching to a container.
1404//
1405// See https://goo.gl/JF10Zk for more details.
1406type AttachToContainerOptions struct {
1407	Container    string    `qs:"-"`
1408	InputStream  io.Reader `qs:"-"`
1409	OutputStream io.Writer `qs:"-"`
1410	ErrorStream  io.Writer `qs:"-"`
1411
1412	// If set, after a successful connect, a sentinel will be sent and then the
1413	// client will block on receive before continuing.
1414	//
1415	// It must be an unbuffered channel. Using a buffered channel can lead
1416	// to unexpected behavior.
1417	Success chan struct{}
1418
1419	// Use raw terminal? Usually true when the container contains a TTY.
1420	RawTerminal bool `qs:"-"`
1421
1422	// Get container logs, sending it to OutputStream.
1423	Logs bool
1424
1425	// Stream the response?
1426	Stream bool
1427
1428	// Attach to stdin, and use InputStream.
1429	Stdin bool
1430
1431	// Attach to stdout, and use OutputStream.
1432	Stdout bool
1433
1434	// Attach to stderr, and use ErrorStream.
1435	Stderr bool
1436}
1437
1438// AttachToContainer attaches to a container, using the given options.
1439//
1440// See https://goo.gl/JF10Zk for more details.
1441func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
1442	cw, err := c.AttachToContainerNonBlocking(opts)
1443	if err != nil {
1444		return err
1445	}
1446	return cw.Wait()
1447}
1448
1449// AttachToContainerNonBlocking attaches to a container, using the given options.
1450// This function does not block.
1451//
1452// See https://goo.gl/NKpkFk for more details.
1453func (c *Client) AttachToContainerNonBlocking(opts AttachToContainerOptions) (CloseWaiter, error) {
1454	if opts.Container == "" {
1455		return nil, &NoSuchContainer{ID: opts.Container}
1456	}
1457	path := "/containers/" + opts.Container + "/attach?" + queryString(opts)
1458	return c.hijack("POST", path, hijackOptions{
1459		success:        opts.Success,
1460		setRawTerminal: opts.RawTerminal,
1461		in:             opts.InputStream,
1462		stdout:         opts.OutputStream,
1463		stderr:         opts.ErrorStream,
1464	})
1465}
1466
1467// LogsOptions represents the set of options used when getting logs from a
1468// container.
1469//
1470// See https://goo.gl/krK0ZH for more details.
1471type LogsOptions struct {
1472	Context           context.Context
1473	Container         string        `qs:"-"`
1474	OutputStream      io.Writer     `qs:"-"`
1475	ErrorStream       io.Writer     `qs:"-"`
1476	InactivityTimeout time.Duration `qs:"-"`
1477	Tail              string
1478
1479	Since      int64
1480	Follow     bool
1481	Stdout     bool
1482	Stderr     bool
1483	Timestamps bool
1484
1485	// Use raw terminal? Usually true when the container contains a TTY.
1486	RawTerminal bool `qs:"-"`
1487}
1488
1489// Logs gets stdout and stderr logs from the specified container.
1490//
1491// When LogsOptions.RawTerminal is set to false, go-dockerclient will multiplex
1492// the streams and send the containers stdout to LogsOptions.OutputStream, and
1493// stderr to LogsOptions.ErrorStream.
1494//
1495// When LogsOptions.RawTerminal is true, callers will get the raw stream on
1496// LogsOptions.OutputStream. The caller can use libraries such as dlog
1497// (github.com/ahmetalpbalkan/dlog).
1498//
1499// See https://goo.gl/krK0ZH for more details.
1500func (c *Client) Logs(opts LogsOptions) error {
1501	if opts.Container == "" {
1502		return &NoSuchContainer{ID: opts.Container}
1503	}
1504	if opts.Tail == "" {
1505		opts.Tail = "all"
1506	}
1507	path := "/containers/" + opts.Container + "/logs?" + queryString(opts)
1508	return c.stream("GET", path, streamOptions{
1509		setRawTerminal:    opts.RawTerminal,
1510		stdout:            opts.OutputStream,
1511		stderr:            opts.ErrorStream,
1512		inactivityTimeout: opts.InactivityTimeout,
1513		context:           opts.Context,
1514	})
1515}
1516
1517// ResizeContainerTTY resizes the terminal to the given height and width.
1518//
1519// See https://goo.gl/FImjeq for more details.
1520func (c *Client) ResizeContainerTTY(id string, height, width int) error {
1521	params := make(url.Values)
1522	params.Set("h", strconv.Itoa(height))
1523	params.Set("w", strconv.Itoa(width))
1524	resp, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), doOptions{})
1525	if err != nil {
1526		return err
1527	}
1528	resp.Body.Close()
1529	return nil
1530}
1531
1532// ExportContainerOptions is the set of parameters to the ExportContainer
1533// method.
1534//
1535// See https://goo.gl/yGJCIh for more details.
1536type ExportContainerOptions struct {
1537	ID                string
1538	OutputStream      io.Writer
1539	InactivityTimeout time.Duration `qs:"-"`
1540	Context           context.Context
1541}
1542
1543// ExportContainer export the contents of container id as tar archive
1544// and prints the exported contents to stdout.
1545//
1546// See https://goo.gl/yGJCIh for more details.
1547func (c *Client) ExportContainer(opts ExportContainerOptions) error {
1548	if opts.ID == "" {
1549		return &NoSuchContainer{ID: opts.ID}
1550	}
1551	url := fmt.Sprintf("/containers/%s/export", opts.ID)
1552	return c.stream("GET", url, streamOptions{
1553		setRawTerminal:    true,
1554		stdout:            opts.OutputStream,
1555		inactivityTimeout: opts.InactivityTimeout,
1556		context:           opts.Context,
1557	})
1558}
1559
1560// PruneContainersOptions specify parameters to the PruneContainers function.
1561//
1562// See https://goo.gl/wnkgDT for more details.
1563type PruneContainersOptions struct {
1564	Filters map[string][]string
1565	Context context.Context
1566}
1567
1568// PruneContainersResults specify results from the PruneContainers function.
1569//
1570// See https://goo.gl/wnkgDT for more details.
1571type PruneContainersResults struct {
1572	ContainersDeleted []string
1573	SpaceReclaimed    int64
1574}
1575
1576// PruneContainers deletes containers which are stopped.
1577//
1578// See https://goo.gl/wnkgDT for more details.
1579func (c *Client) PruneContainers(opts PruneContainersOptions) (*PruneContainersResults, error) {
1580	path := "/containers/prune?" + queryString(opts)
1581	resp, err := c.do("POST", path, doOptions{context: opts.Context})
1582	if err != nil {
1583		return nil, err
1584	}
1585	defer resp.Body.Close()
1586	var results PruneContainersResults
1587	if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
1588		return nil, err
1589	}
1590	return &results, nil
1591}
1592
1593// NoSuchContainer is the error returned when a given container does not exist.
1594type NoSuchContainer struct {
1595	ID  string
1596	Err error
1597}
1598
1599func (err *NoSuchContainer) Error() string {
1600	if err.Err != nil {
1601		return err.Err.Error()
1602	}
1603	return "No such container: " + err.ID
1604}
1605
1606// ContainerAlreadyRunning is the error returned when a given container is
1607// already running.
1608type ContainerAlreadyRunning struct {
1609	ID string
1610}
1611
1612func (err *ContainerAlreadyRunning) Error() string {
1613	return "Container already running: " + err.ID
1614}
1615
1616// ContainerNotRunning is the error returned when a given container is not
1617// running.
1618type ContainerNotRunning struct {
1619	ID string
1620}
1621
1622func (err *ContainerNotRunning) Error() string {
1623	return "Container not running: " + err.ID
1624}
1625