diff options
author | Alex Pilon <apilon@hashicorp.com> | 2019-02-22 18:24:22 -0500 |
---|---|---|
committer | Alex Pilon <apilon@hashicorp.com> | 2019-02-22 18:24:22 -0500 |
commit | 07971ca38143c5faf951d152fba370ddcbe26ad5 (patch) | |
tree | 09af1ef91c782de921efc83113907184c8adecee /vendor/github.com/fsouza/go-dockerclient/container.go | |
parent | 9b6f05970a001f683b9c886c652de81f00cd5f00 (diff) | |
download | terraform-provider-statuscake-07971ca38143c5faf951d152fba370ddcbe26ad5.tar.gz terraform-provider-statuscake-07971ca38143c5faf951d152fba370ddcbe26ad5.tar.zst terraform-provider-statuscake-07971ca38143c5faf951d152fba370ddcbe26ad5.zip |
deps: use go modules for dep mgmt
run go mod tidy
remove govendor from makefile and travis config
set appropriate env vars for go modules
Diffstat (limited to 'vendor/github.com/fsouza/go-dockerclient/container.go')
-rw-r--r-- | vendor/github.com/fsouza/go-dockerclient/container.go | 1325 |
1 files changed, 0 insertions, 1325 deletions
diff --git a/vendor/github.com/fsouza/go-dockerclient/container.go b/vendor/github.com/fsouza/go-dockerclient/container.go deleted file mode 100644 index f7ed5f5..0000000 --- a/vendor/github.com/fsouza/go-dockerclient/container.go +++ /dev/null | |||
@@ -1,1325 +0,0 @@ | |||
1 | // Copyright 2015 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 | |||
5 | package docker | ||
6 | |||
7 | import ( | ||
8 | "encoding/json" | ||
9 | "errors" | ||
10 | "fmt" | ||
11 | "io" | ||
12 | "net/http" | ||
13 | "net/url" | ||
14 | "strconv" | ||
15 | "strings" | ||
16 | "time" | ||
17 | |||
18 | "github.com/fsouza/go-dockerclient/external/github.com/docker/go-units" | ||
19 | ) | ||
20 | |||
21 | // ErrContainerAlreadyExists is the error returned by CreateContainer when the | ||
22 | // container already exists. | ||
23 | var ErrContainerAlreadyExists = errors.New("container already exists") | ||
24 | |||
25 | // ListContainersOptions specify parameters to the ListContainers function. | ||
26 | // | ||
27 | // See https://goo.gl/47a6tO for more details. | ||
28 | type ListContainersOptions struct { | ||
29 | All bool | ||
30 | Size bool | ||
31 | Limit int | ||
32 | Since string | ||
33 | Before string | ||
34 | Filters map[string][]string | ||
35 | } | ||
36 | |||
37 | // APIPort is a type that represents a port mapping returned by the Docker API | ||
38 | type APIPort struct { | ||
39 | PrivatePort int64 `json:"PrivatePort,omitempty" yaml:"PrivatePort,omitempty"` | ||
40 | PublicPort int64 `json:"PublicPort,omitempty" yaml:"PublicPort,omitempty"` | ||
41 | Type string `json:"Type,omitempty" yaml:"Type,omitempty"` | ||
42 | IP string `json:"IP,omitempty" yaml:"IP,omitempty"` | ||
43 | } | ||
44 | |||
45 | // APIMount represents a mount point for a container. | ||
46 | type APIMount struct { | ||
47 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
48 | Source string `json:"Source,omitempty" yaml:"Source,omitempty"` | ||
49 | Destination string `json:"Destination,omitempty" yaml:"Destination,omitempty"` | ||
50 | Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"` | ||
51 | Mode string `json:"Mode,omitempty" yaml:"Mode,omitempty"` | ||
52 | RW bool `json:"RW,omitempty" yaml:"RW,omitempty"` | ||
53 | Propogation string `json:"Propogation,omitempty" yaml:"Propogation,omitempty"` | ||
54 | } | ||
55 | |||
56 | // APIContainers represents each container in the list returned by | ||
57 | // ListContainers. | ||
58 | type APIContainers struct { | ||
59 | ID string `json:"Id" yaml:"Id"` | ||
60 | Image string `json:"Image,omitempty" yaml:"Image,omitempty"` | ||
61 | Command string `json:"Command,omitempty" yaml:"Command,omitempty"` | ||
62 | Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"` | ||
63 | State string `json:"State,omitempty" yaml:"State,omitempty"` | ||
64 | Status string `json:"Status,omitempty" yaml:"Status,omitempty"` | ||
65 | Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"` | ||
66 | SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"` | ||
67 | SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"` | ||
68 | Names []string `json:"Names,omitempty" yaml:"Names,omitempty"` | ||
69 | Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"` | ||
70 | Networks NetworkList `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"` | ||
71 | Mounts []APIMount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"` | ||
72 | } | ||
73 | |||
74 | // NetworkList encapsulates a map of networks, as returned by the Docker API in | ||
75 | // ListContainers. | ||
76 | type NetworkList struct { | ||
77 | Networks map[string]ContainerNetwork `json:"Networks" yaml:"Networks,omitempty"` | ||
78 | } | ||
79 | |||
80 | // ListContainers returns a slice of containers matching the given criteria. | ||
81 | // | ||
82 | // See https://goo.gl/47a6tO for more details. | ||
83 | func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) { | ||
84 | path := "/containers/json?" + queryString(opts) | ||
85 | resp, err := c.do("GET", path, doOptions{}) | ||
86 | if err != nil { | ||
87 | return nil, err | ||
88 | } | ||
89 | defer resp.Body.Close() | ||
90 | var containers []APIContainers | ||
91 | if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil { | ||
92 | return nil, err | ||
93 | } | ||
94 | return containers, nil | ||
95 | } | ||
96 | |||
97 | // Port represents the port number and the protocol, in the form | ||
98 | // <number>/<protocol>. For example: 80/tcp. | ||
99 | type Port string | ||
100 | |||
101 | // Port returns the number of the port. | ||
102 | func (p Port) Port() string { | ||
103 | return strings.Split(string(p), "/")[0] | ||
104 | } | ||
105 | |||
106 | // Proto returns the name of the protocol. | ||
107 | func (p Port) Proto() string { | ||
108 | parts := strings.Split(string(p), "/") | ||
109 | if len(parts) == 1 { | ||
110 | return "tcp" | ||
111 | } | ||
112 | return parts[1] | ||
113 | } | ||
114 | |||
115 | // State represents the state of a container. | ||
116 | type State struct { | ||
117 | Status string `json:"Status,omitempty" yaml:"Status,omitempty"` | ||
118 | Running bool `json:"Running,omitempty" yaml:"Running,omitempty"` | ||
119 | Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"` | ||
120 | Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"` | ||
121 | OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty"` | ||
122 | RemovalInProgress bool `json:"RemovalInProgress,omitempty" yaml:"RemovalInProgress,omitempty"` | ||
123 | Dead bool `json:"Dead,omitempty" yaml:"Dead,omitempty"` | ||
124 | Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"` | ||
125 | ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"` | ||
126 | Error string `json:"Error,omitempty" yaml:"Error,omitempty"` | ||
127 | StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"` | ||
128 | FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"` | ||
129 | } | ||
130 | |||
131 | // String returns a human-readable description of the state | ||
132 | func (s *State) String() string { | ||
133 | if s.Running { | ||
134 | if s.Paused { | ||
135 | return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) | ||
136 | } | ||
137 | if s.Restarting { | ||
138 | return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) | ||
139 | } | ||
140 | |||
141 | return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) | ||
142 | } | ||
143 | |||
144 | if s.RemovalInProgress { | ||
145 | return "Removal In Progress" | ||
146 | } | ||
147 | |||
148 | if s.Dead { | ||
149 | return "Dead" | ||
150 | } | ||
151 | |||
152 | if s.StartedAt.IsZero() { | ||
153 | return "Created" | ||
154 | } | ||
155 | |||
156 | if s.FinishedAt.IsZero() { | ||
157 | return "" | ||
158 | } | ||
159 | |||
160 | return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) | ||
161 | } | ||
162 | |||
163 | // StateString returns a single string to describe state | ||
164 | func (s *State) StateString() string { | ||
165 | if s.Running { | ||
166 | if s.Paused { | ||
167 | return "paused" | ||
168 | } | ||
169 | if s.Restarting { | ||
170 | return "restarting" | ||
171 | } | ||
172 | return "running" | ||
173 | } | ||
174 | |||
175 | if s.Dead { | ||
176 | return "dead" | ||
177 | } | ||
178 | |||
179 | if s.StartedAt.IsZero() { | ||
180 | return "created" | ||
181 | } | ||
182 | |||
183 | return "exited" | ||
184 | } | ||
185 | |||
186 | // PortBinding represents the host/container port mapping as returned in the | ||
187 | // `docker inspect` json | ||
188 | type PortBinding struct { | ||
189 | HostIP string `json:"HostIP,omitempty" yaml:"HostIP,omitempty"` | ||
190 | HostPort string `json:"HostPort,omitempty" yaml:"HostPort,omitempty"` | ||
191 | } | ||
192 | |||
193 | // PortMapping represents a deprecated field in the `docker inspect` output, | ||
194 | // and its value as found in NetworkSettings should always be nil | ||
195 | type PortMapping map[string]string | ||
196 | |||
197 | // ContainerNetwork represents the networking settings of a container per network. | ||
198 | type ContainerNetwork struct { | ||
199 | MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"` | ||
200 | GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty"` | ||
201 | GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty"` | ||
202 | IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty"` | ||
203 | IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty"` | ||
204 | IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"` | ||
205 | Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"` | ||
206 | EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"` | ||
207 | NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"` | ||
208 | } | ||
209 | |||
210 | // NetworkSettings contains network-related information about a container | ||
211 | type NetworkSettings struct { | ||
212 | Networks map[string]ContainerNetwork `json:"Networks,omitempty" yaml:"Networks,omitempty"` | ||
213 | IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"` | ||
214 | IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty"` | ||
215 | MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"` | ||
216 | Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"` | ||
217 | Bridge string `json:"Bridge,omitempty" yaml:"Bridge,omitempty"` | ||
218 | PortMapping map[string]PortMapping `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty"` | ||
219 | Ports map[Port][]PortBinding `json:"Ports,omitempty" yaml:"Ports,omitempty"` | ||
220 | NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"` | ||
221 | EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"` | ||
222 | SandboxKey string `json:"SandboxKey,omitempty" yaml:"SandboxKey,omitempty"` | ||
223 | GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty"` | ||
224 | GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty"` | ||
225 | IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty"` | ||
226 | LinkLocalIPv6Address string `json:"LinkLocalIPv6Address,omitempty" yaml:"LinkLocalIPv6Address,omitempty"` | ||
227 | LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen,omitempty" yaml:"LinkLocalIPv6PrefixLen,omitempty"` | ||
228 | SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty" yaml:"SecondaryIPAddresses,omitempty"` | ||
229 | SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty" yaml:"SecondaryIPv6Addresses,omitempty"` | ||
230 | } | ||
231 | |||
232 | // PortMappingAPI translates the port mappings as contained in NetworkSettings | ||
233 | // into the format in which they would appear when returned by the API | ||
234 | func (settings *NetworkSettings) PortMappingAPI() []APIPort { | ||
235 | var mapping []APIPort | ||
236 | for port, bindings := range settings.Ports { | ||
237 | p, _ := parsePort(port.Port()) | ||
238 | if len(bindings) == 0 { | ||
239 | mapping = append(mapping, APIPort{ | ||
240 | PrivatePort: int64(p), | ||
241 | Type: port.Proto(), | ||
242 | }) | ||
243 | continue | ||
244 | } | ||
245 | for _, binding := range bindings { | ||
246 | p, _ := parsePort(port.Port()) | ||
247 | h, _ := parsePort(binding.HostPort) | ||
248 | mapping = append(mapping, APIPort{ | ||
249 | PrivatePort: int64(p), | ||
250 | PublicPort: int64(h), | ||
251 | Type: port.Proto(), | ||
252 | IP: binding.HostIP, | ||
253 | }) | ||
254 | } | ||
255 | } | ||
256 | return mapping | ||
257 | } | ||
258 | |||
259 | func parsePort(rawPort string) (int, error) { | ||
260 | port, err := strconv.ParseUint(rawPort, 10, 16) | ||
261 | if err != nil { | ||
262 | return 0, err | ||
263 | } | ||
264 | return int(port), nil | ||
265 | } | ||
266 | |||
267 | // Config is the list of configuration options used when creating a container. | ||
268 | // Config does not contain the options that are specific to starting a container on a | ||
269 | // given host. Those are contained in HostConfig | ||
270 | type Config struct { | ||
271 | Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty"` | ||
272 | Domainname string `json:"Domainname,omitempty" yaml:"Domainname,omitempty"` | ||
273 | User string `json:"User,omitempty" yaml:"User,omitempty"` | ||
274 | Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"` | ||
275 | MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"` | ||
276 | MemoryReservation int64 `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty"` | ||
277 | KernelMemory int64 `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty"` | ||
278 | PidsLimit int64 `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty"` | ||
279 | CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"` | ||
280 | CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"` | ||
281 | AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"` | ||
282 | AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"` | ||
283 | AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"` | ||
284 | PortSpecs []string `json:"PortSpecs,omitempty" yaml:"PortSpecs,omitempty"` | ||
285 | ExposedPorts map[Port]struct{} `json:"ExposedPorts,omitempty" yaml:"ExposedPorts,omitempty"` | ||
286 | StopSignal string `json:"StopSignal,omitempty" yaml:"StopSignal,omitempty"` | ||
287 | Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"` | ||
288 | OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"` | ||
289 | StdinOnce bool `json:"StdinOnce,omitempty" yaml:"StdinOnce,omitempty"` | ||
290 | Env []string `json:"Env,omitempty" yaml:"Env,omitempty"` | ||
291 | Cmd []string `json:"Cmd" yaml:"Cmd"` | ||
292 | DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.9 and below only | ||
293 | Image string `json:"Image,omitempty" yaml:"Image,omitempty"` | ||
294 | Volumes map[string]struct{} `json:"Volumes,omitempty" yaml:"Volumes,omitempty"` | ||
295 | VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"` | ||
296 | VolumesFrom string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"` | ||
297 | WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty"` | ||
298 | MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"` | ||
299 | Entrypoint []string `json:"Entrypoint" yaml:"Entrypoint"` | ||
300 | NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"` | ||
301 | SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty"` | ||
302 | OnBuild []string `json:"OnBuild,omitempty" yaml:"OnBuild,omitempty"` | ||
303 | Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"` | ||
304 | Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"` | ||
305 | } | ||
306 | |||
307 | // Mount represents a mount point in the container. | ||
308 | // | ||
309 | // It has been added in the version 1.20 of the Docker API, available since | ||
310 | // Docker 1.8. | ||
311 | type Mount struct { | ||
312 | Name string | ||
313 | Source string | ||
314 | Destination string | ||
315 | Driver string | ||
316 | Mode string | ||
317 | RW bool | ||
318 | } | ||
319 | |||
320 | // LogConfig defines the log driver type and the configuration for it. | ||
321 | type LogConfig struct { | ||
322 | Type string `json:"Type,omitempty" yaml:"Type,omitempty"` | ||
323 | Config map[string]string `json:"Config,omitempty" yaml:"Config,omitempty"` | ||
324 | } | ||
325 | |||
326 | // ULimit defines system-wide resource limitations | ||
327 | // This can help a lot in system administration, e.g. when a user starts too many processes and therefore makes the system unresponsive for other users. | ||
328 | type ULimit struct { | ||
329 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
330 | Soft int64 `json:"Soft,omitempty" yaml:"Soft,omitempty"` | ||
331 | Hard int64 `json:"Hard,omitempty" yaml:"Hard,omitempty"` | ||
332 | } | ||
333 | |||
334 | // SwarmNode containers information about which Swarm node the container is on | ||
335 | type SwarmNode struct { | ||
336 | ID string `json:"ID,omitempty" yaml:"ID,omitempty"` | ||
337 | IP string `json:"IP,omitempty" yaml:"IP,omitempty"` | ||
338 | Addr string `json:"Addr,omitempty" yaml:"Addr,omitempty"` | ||
339 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
340 | CPUs int64 `json:"CPUs,omitempty" yaml:"CPUs,omitempty"` | ||
341 | Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"` | ||
342 | Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"` | ||
343 | } | ||
344 | |||
345 | // GraphDriver contains information about the GraphDriver used by the container | ||
346 | type GraphDriver struct { | ||
347 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
348 | Data map[string]string `json:"Data,omitempty" yaml:"Data,omitempty"` | ||
349 | } | ||
350 | |||
351 | // Container is the type encompasing everything about a container - its config, | ||
352 | // hostconfig, etc. | ||
353 | type Container struct { | ||
354 | ID string `json:"Id" yaml:"Id"` | ||
355 | |||
356 | Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"` | ||
357 | |||
358 | Path string `json:"Path,omitempty" yaml:"Path,omitempty"` | ||
359 | Args []string `json:"Args,omitempty" yaml:"Args,omitempty"` | ||
360 | |||
361 | Config *Config `json:"Config,omitempty" yaml:"Config,omitempty"` | ||
362 | State State `json:"State,omitempty" yaml:"State,omitempty"` | ||
363 | Image string `json:"Image,omitempty" yaml:"Image,omitempty"` | ||
364 | |||
365 | Node *SwarmNode `json:"Node,omitempty" yaml:"Node,omitempty"` | ||
366 | |||
367 | NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"` | ||
368 | |||
369 | SysInitPath string `json:"SysInitPath,omitempty" yaml:"SysInitPath,omitempty"` | ||
370 | ResolvConfPath string `json:"ResolvConfPath,omitempty" yaml:"ResolvConfPath,omitempty"` | ||
371 | HostnamePath string `json:"HostnamePath,omitempty" yaml:"HostnamePath,omitempty"` | ||
372 | HostsPath string `json:"HostsPath,omitempty" yaml:"HostsPath,omitempty"` | ||
373 | LogPath string `json:"LogPath,omitempty" yaml:"LogPath,omitempty"` | ||
374 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
375 | Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"` | ||
376 | Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"` | ||
377 | |||
378 | Volumes map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty"` | ||
379 | VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty"` | ||
380 | HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"` | ||
381 | ExecIDs []string `json:"ExecIDs,omitempty" yaml:"ExecIDs,omitempty"` | ||
382 | GraphDriver *GraphDriver `json:"GraphDriver,omitempty" yaml:"GraphDriver,omitempty"` | ||
383 | |||
384 | RestartCount int `json:"RestartCount,omitempty" yaml:"RestartCount,omitempty"` | ||
385 | |||
386 | AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty"` | ||
387 | } | ||
388 | |||
389 | // UpdateContainerOptions specify parameters to the UpdateContainer function. | ||
390 | // | ||
391 | // See https://goo.gl/Y6fXUy for more details. | ||
392 | type UpdateContainerOptions struct { | ||
393 | BlkioWeight int `json:"BlkioWeight"` | ||
394 | CPUShares int `json:"CpuShares"` | ||
395 | CPUPeriod int `json:"CpuPeriod"` | ||
396 | CPUQuota int `json:"CpuQuota"` | ||
397 | CpusetCpus string `json:"CpusetCpus"` | ||
398 | CpusetMems string `json:"CpusetMems"` | ||
399 | Memory int `json:"Memory"` | ||
400 | MemorySwap int `json:"MemorySwap"` | ||
401 | MemoryReservation int `json:"MemoryReservation"` | ||
402 | KernelMemory int `json:"KernelMemory"` | ||
403 | RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty"` | ||
404 | } | ||
405 | |||
406 | // UpdateContainer updates the container at ID with the options | ||
407 | // | ||
408 | // See https://goo.gl/Y6fXUy for more details. | ||
409 | func (c *Client) UpdateContainer(id string, opts UpdateContainerOptions) error { | ||
410 | resp, err := c.do("POST", fmt.Sprintf("/containers/"+id+"/update"), doOptions{data: opts, forceJSON: true}) | ||
411 | if err != nil { | ||
412 | return err | ||
413 | } | ||
414 | defer resp.Body.Close() | ||
415 | return nil | ||
416 | } | ||
417 | |||
418 | // RenameContainerOptions specify parameters to the RenameContainer function. | ||
419 | // | ||
420 | // See https://goo.gl/laSOIy for more details. | ||
421 | type RenameContainerOptions struct { | ||
422 | // ID of container to rename | ||
423 | ID string `qs:"-"` | ||
424 | |||
425 | // New name | ||
426 | Name string `json:"name,omitempty" yaml:"name,omitempty"` | ||
427 | } | ||
428 | |||
429 | // RenameContainer updates and existing containers name | ||
430 | // | ||
431 | // See https://goo.gl/laSOIy for more details. | ||
432 | func (c *Client) RenameContainer(opts RenameContainerOptions) error { | ||
433 | resp, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{}) | ||
434 | if err != nil { | ||
435 | return err | ||
436 | } | ||
437 | resp.Body.Close() | ||
438 | return nil | ||
439 | } | ||
440 | |||
441 | // InspectContainer returns information about a container by its ID. | ||
442 | // | ||
443 | // See https://goo.gl/RdIq0b for more details. | ||
444 | func (c *Client) InspectContainer(id string) (*Container, error) { | ||
445 | path := "/containers/" + id + "/json" | ||
446 | resp, err := c.do("GET", path, doOptions{}) | ||
447 | if err != nil { | ||
448 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
449 | return nil, &NoSuchContainer{ID: id} | ||
450 | } | ||
451 | return nil, err | ||
452 | } | ||
453 | defer resp.Body.Close() | ||
454 | var container Container | ||
455 | if err := json.NewDecoder(resp.Body).Decode(&container); err != nil { | ||
456 | return nil, err | ||
457 | } | ||
458 | return &container, nil | ||
459 | } | ||
460 | |||
461 | // ContainerChanges returns changes in the filesystem of the given container. | ||
462 | // | ||
463 | // See https://goo.gl/9GsTIF for more details. | ||
464 | func (c *Client) ContainerChanges(id string) ([]Change, error) { | ||
465 | path := "/containers/" + id + "/changes" | ||
466 | resp, err := c.do("GET", path, doOptions{}) | ||
467 | if err != nil { | ||
468 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
469 | return nil, &NoSuchContainer{ID: id} | ||
470 | } | ||
471 | return nil, err | ||
472 | } | ||
473 | defer resp.Body.Close() | ||
474 | var changes []Change | ||
475 | if err := json.NewDecoder(resp.Body).Decode(&changes); err != nil { | ||
476 | return nil, err | ||
477 | } | ||
478 | return changes, nil | ||
479 | } | ||
480 | |||
481 | // CreateContainerOptions specify parameters to the CreateContainer function. | ||
482 | // | ||
483 | // See https://goo.gl/WxQzrr for more details. | ||
484 | type CreateContainerOptions struct { | ||
485 | Name string | ||
486 | Config *Config `qs:"-"` | ||
487 | HostConfig *HostConfig `qs:"-"` | ||
488 | } | ||
489 | |||
490 | // CreateContainer creates a new container, returning the container instance, | ||
491 | // or an error in case of failure. | ||
492 | // | ||
493 | // See https://goo.gl/WxQzrr for more details. | ||
494 | func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) { | ||
495 | path := "/containers/create?" + queryString(opts) | ||
496 | resp, err := c.do( | ||
497 | "POST", | ||
498 | path, | ||
499 | doOptions{ | ||
500 | data: struct { | ||
501 | *Config | ||
502 | HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"` | ||
503 | }{ | ||
504 | opts.Config, | ||
505 | opts.HostConfig, | ||
506 | }, | ||
507 | }, | ||
508 | ) | ||
509 | |||
510 | if e, ok := err.(*Error); ok { | ||
511 | if e.Status == http.StatusNotFound { | ||
512 | return nil, ErrNoSuchImage | ||
513 | } | ||
514 | if e.Status == http.StatusConflict { | ||
515 | return nil, ErrContainerAlreadyExists | ||
516 | } | ||
517 | } | ||
518 | |||
519 | if err != nil { | ||
520 | return nil, err | ||
521 | } | ||
522 | defer resp.Body.Close() | ||
523 | var container Container | ||
524 | if err := json.NewDecoder(resp.Body).Decode(&container); err != nil { | ||
525 | return nil, err | ||
526 | } | ||
527 | |||
528 | container.Name = opts.Name | ||
529 | |||
530 | return &container, nil | ||
531 | } | ||
532 | |||
533 | // KeyValuePair is a type for generic key/value pairs as used in the Lxc | ||
534 | // configuration | ||
535 | type KeyValuePair struct { | ||
536 | Key string `json:"Key,omitempty" yaml:"Key,omitempty"` | ||
537 | Value string `json:"Value,omitempty" yaml:"Value,omitempty"` | ||
538 | } | ||
539 | |||
540 | // RestartPolicy represents the policy for automatically restarting a container. | ||
541 | // | ||
542 | // Possible values are: | ||
543 | // | ||
544 | // - always: the docker daemon will always restart the container | ||
545 | // - on-failure: the docker daemon will restart the container on failures, at | ||
546 | // most MaximumRetryCount times | ||
547 | // - no: the docker daemon will not restart the container automatically | ||
548 | type RestartPolicy struct { | ||
549 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
550 | MaximumRetryCount int `json:"MaximumRetryCount,omitempty" yaml:"MaximumRetryCount,omitempty"` | ||
551 | } | ||
552 | |||
553 | // AlwaysRestart returns a restart policy that tells the Docker daemon to | ||
554 | // always restart the container. | ||
555 | func AlwaysRestart() RestartPolicy { | ||
556 | return RestartPolicy{Name: "always"} | ||
557 | } | ||
558 | |||
559 | // RestartOnFailure returns a restart policy that tells the Docker daemon to | ||
560 | // restart the container on failures, trying at most maxRetry times. | ||
561 | func RestartOnFailure(maxRetry int) RestartPolicy { | ||
562 | return RestartPolicy{Name: "on-failure", MaximumRetryCount: maxRetry} | ||
563 | } | ||
564 | |||
565 | // NeverRestart returns a restart policy that tells the Docker daemon to never | ||
566 | // restart the container on failures. | ||
567 | func NeverRestart() RestartPolicy { | ||
568 | return RestartPolicy{Name: "no"} | ||
569 | } | ||
570 | |||
571 | // Device represents a device mapping between the Docker host and the | ||
572 | // container. | ||
573 | type Device struct { | ||
574 | PathOnHost string `json:"PathOnHost,omitempty" yaml:"PathOnHost,omitempty"` | ||
575 | PathInContainer string `json:"PathInContainer,omitempty" yaml:"PathInContainer,omitempty"` | ||
576 | CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty"` | ||
577 | } | ||
578 | |||
579 | // BlockWeight represents a relative device weight for an individual device inside | ||
580 | // of a container | ||
581 | // | ||
582 | // See https://goo.gl/FSdP0H for more details. | ||
583 | type BlockWeight struct { | ||
584 | Path string `json:"Path,omitempty"` | ||
585 | Weight string `json:"Weight,omitempty"` | ||
586 | } | ||
587 | |||
588 | // BlockLimit represents a read/write limit in IOPS or Bandwidth for a device | ||
589 | // inside of a container | ||
590 | // | ||
591 | // See https://goo.gl/FSdP0H for more details. | ||
592 | type BlockLimit struct { | ||
593 | Path string `json:"Path,omitempty"` | ||
594 | Rate string `json:"Rate,omitempty"` | ||
595 | } | ||
596 | |||
597 | // HostConfig contains the container options related to starting a container on | ||
598 | // a given host | ||
599 | type HostConfig struct { | ||
600 | Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"` | ||
601 | CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"` | ||
602 | CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"` | ||
603 | GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty"` | ||
604 | ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"` | ||
605 | LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"` | ||
606 | Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"` | ||
607 | PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"` | ||
608 | Links []string `json:"Links,omitempty" yaml:"Links,omitempty"` | ||
609 | PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"` | ||
610 | DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only | ||
611 | DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty"` | ||
612 | DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"` | ||
613 | ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"` | ||
614 | VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"` | ||
615 | UsernsMode string `json:"UsernsMode,omitempty" yaml:"UsernsMode,omitempty"` | ||
616 | NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"` | ||
617 | IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"` | ||
618 | PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"` | ||
619 | UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"` | ||
620 | RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"` | ||
621 | Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"` | ||
622 | LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"` | ||
623 | ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"` | ||
624 | SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"` | ||
625 | CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"` | ||
626 | Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"` | ||
627 | MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"` | ||
628 | MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"` | ||
629 | OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"` | ||
630 | CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"` | ||
631 | CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"` | ||
632 | CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"` | ||
633 | CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"` | ||
634 | CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"` | ||
635 | CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"` | ||
636 | BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"` | ||
637 | BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice"` | ||
638 | BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps"` | ||
639 | BlkioDeviceReadIOps []BlockLimit `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps"` | ||
640 | BlkioDeviceWriteBps []BlockLimit `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps"` | ||
641 | BlkioDeviceWriteIOps []BlockLimit `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps"` | ||
642 | Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"` | ||
643 | VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"` | ||
644 | OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"` | ||
645 | ShmSize int64 `json:"ShmSize,omitempty" yaml:"ShmSize,omitempty"` | ||
646 | } | ||
647 | |||
648 | // StartContainer starts a container, returning an error in case of failure. | ||
649 | // | ||
650 | // See https://goo.gl/MrBAJv for more details. | ||
651 | func (c *Client) StartContainer(id string, hostConfig *HostConfig) error { | ||
652 | path := "/containers/" + id + "/start" | ||
653 | resp, err := c.do("POST", path, doOptions{data: hostConfig, forceJSON: true}) | ||
654 | if err != nil { | ||
655 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
656 | return &NoSuchContainer{ID: id, Err: err} | ||
657 | } | ||
658 | return err | ||
659 | } | ||
660 | if resp.StatusCode == http.StatusNotModified { | ||
661 | return &ContainerAlreadyRunning{ID: id} | ||
662 | } | ||
663 | resp.Body.Close() | ||
664 | return nil | ||
665 | } | ||
666 | |||
667 | // StopContainer stops a container, killing it after the given timeout (in | ||
668 | // seconds). | ||
669 | // | ||
670 | // See https://goo.gl/USqsFt for more details. | ||
671 | func (c *Client) StopContainer(id string, timeout uint) error { | ||
672 | path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout) | ||
673 | resp, err := c.do("POST", path, doOptions{}) | ||
674 | if err != nil { | ||
675 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
676 | return &NoSuchContainer{ID: id} | ||
677 | } | ||
678 | return err | ||
679 | } | ||
680 | if resp.StatusCode == http.StatusNotModified { | ||
681 | return &ContainerNotRunning{ID: id} | ||
682 | } | ||
683 | resp.Body.Close() | ||
684 | return nil | ||
685 | } | ||
686 | |||
687 | // RestartContainer stops a container, killing it after the given timeout (in | ||
688 | // seconds), during the stop process. | ||
689 | // | ||
690 | // See https://goo.gl/QzsDnz for more details. | ||
691 | func (c *Client) RestartContainer(id string, timeout uint) error { | ||
692 | path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout) | ||
693 | resp, err := c.do("POST", path, doOptions{}) | ||
694 | if err != nil { | ||
695 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
696 | return &NoSuchContainer{ID: id} | ||
697 | } | ||
698 | return err | ||
699 | } | ||
700 | resp.Body.Close() | ||
701 | return nil | ||
702 | } | ||
703 | |||
704 | // PauseContainer pauses the given container. | ||
705 | // | ||
706 | // See https://goo.gl/OF7W9X for more details. | ||
707 | func (c *Client) PauseContainer(id string) error { | ||
708 | path := fmt.Sprintf("/containers/%s/pause", id) | ||
709 | resp, err := c.do("POST", path, doOptions{}) | ||
710 | if err != nil { | ||
711 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
712 | return &NoSuchContainer{ID: id} | ||
713 | } | ||
714 | return err | ||
715 | } | ||
716 | resp.Body.Close() | ||
717 | return nil | ||
718 | } | ||
719 | |||
720 | // UnpauseContainer unpauses the given container. | ||
721 | // | ||
722 | // See https://goo.gl/7dwyPA for more details. | ||
723 | func (c *Client) UnpauseContainer(id string) error { | ||
724 | path := fmt.Sprintf("/containers/%s/unpause", id) | ||
725 | resp, err := c.do("POST", path, doOptions{}) | ||
726 | if err != nil { | ||
727 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
728 | return &NoSuchContainer{ID: id} | ||
729 | } | ||
730 | return err | ||
731 | } | ||
732 | resp.Body.Close() | ||
733 | return nil | ||
734 | } | ||
735 | |||
736 | // TopResult represents the list of processes running in a container, as | ||
737 | // returned by /containers/<id>/top. | ||
738 | // | ||
739 | // See https://goo.gl/Rb46aY for more details. | ||
740 | type TopResult struct { | ||
741 | Titles []string | ||
742 | Processes [][]string | ||
743 | } | ||
744 | |||
745 | // TopContainer returns processes running inside a container | ||
746 | // | ||
747 | // See https://goo.gl/Rb46aY for more details. | ||
748 | func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) { | ||
749 | var args string | ||
750 | var result TopResult | ||
751 | if psArgs != "" { | ||
752 | args = fmt.Sprintf("?ps_args=%s", psArgs) | ||
753 | } | ||
754 | path := fmt.Sprintf("/containers/%s/top%s", id, args) | ||
755 | resp, err := c.do("GET", path, doOptions{}) | ||
756 | if err != nil { | ||
757 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
758 | return result, &NoSuchContainer{ID: id} | ||
759 | } | ||
760 | return result, err | ||
761 | } | ||
762 | defer resp.Body.Close() | ||
763 | if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { | ||
764 | return result, err | ||
765 | } | ||
766 | return result, nil | ||
767 | } | ||
768 | |||
769 | // Stats represents container statistics, returned by /containers/<id>/stats. | ||
770 | // | ||
771 | // See https://goo.gl/GNmLHb for more details. | ||
772 | type Stats struct { | ||
773 | Read time.Time `json:"read,omitempty" yaml:"read,omitempty"` | ||
774 | PidsStats struct { | ||
775 | Current uint64 `json:"current,omitempty" yaml:"current,omitempty"` | ||
776 | } `json:"pids_stats,omitempty" yaml:"pids_stats,omitempty"` | ||
777 | Network NetworkStats `json:"network,omitempty" yaml:"network,omitempty"` | ||
778 | Networks map[string]NetworkStats `json:"networks,omitempty" yaml:"networks,omitempty"` | ||
779 | MemoryStats struct { | ||
780 | Stats struct { | ||
781 | TotalPgmafault uint64 `json:"total_pgmafault,omitempty" yaml:"total_pgmafault,omitempty"` | ||
782 | Cache uint64 `json:"cache,omitempty" yaml:"cache,omitempty"` | ||
783 | MappedFile uint64 `json:"mapped_file,omitempty" yaml:"mapped_file,omitempty"` | ||
784 | TotalInactiveFile uint64 `json:"total_inactive_file,omitempty" yaml:"total_inactive_file,omitempty"` | ||
785 | Pgpgout uint64 `json:"pgpgout,omitempty" yaml:"pgpgout,omitempty"` | ||
786 | Rss uint64 `json:"rss,omitempty" yaml:"rss,omitempty"` | ||
787 | TotalMappedFile uint64 `json:"total_mapped_file,omitempty" yaml:"total_mapped_file,omitempty"` | ||
788 | Writeback uint64 `json:"writeback,omitempty" yaml:"writeback,omitempty"` | ||
789 | Unevictable uint64 `json:"unevictable,omitempty" yaml:"unevictable,omitempty"` | ||
790 | Pgpgin uint64 `json:"pgpgin,omitempty" yaml:"pgpgin,omitempty"` | ||
791 | TotalUnevictable uint64 `json:"total_unevictable,omitempty" yaml:"total_unevictable,omitempty"` | ||
792 | Pgmajfault uint64 `json:"pgmajfault,omitempty" yaml:"pgmajfault,omitempty"` | ||
793 | TotalRss uint64 `json:"total_rss,omitempty" yaml:"total_rss,omitempty"` | ||
794 | TotalRssHuge uint64 `json:"total_rss_huge,omitempty" yaml:"total_rss_huge,omitempty"` | ||
795 | TotalWriteback uint64 `json:"total_writeback,omitempty" yaml:"total_writeback,omitempty"` | ||
796 | TotalInactiveAnon uint64 `json:"total_inactive_anon,omitempty" yaml:"total_inactive_anon,omitempty"` | ||
797 | RssHuge uint64 `json:"rss_huge,omitempty" yaml:"rss_huge,omitempty"` | ||
798 | HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit,omitempty" yaml:"hierarchical_memory_limit,omitempty"` | ||
799 | TotalPgfault uint64 `json:"total_pgfault,omitempty" yaml:"total_pgfault,omitempty"` | ||
800 | TotalActiveFile uint64 `json:"total_active_file,omitempty" yaml:"total_active_file,omitempty"` | ||
801 | ActiveAnon uint64 `json:"active_anon,omitempty" yaml:"active_anon,omitempty"` | ||
802 | TotalActiveAnon uint64 `json:"total_active_anon,omitempty" yaml:"total_active_anon,omitempty"` | ||
803 | TotalPgpgout uint64 `json:"total_pgpgout,omitempty" yaml:"total_pgpgout,omitempty"` | ||
804 | TotalCache uint64 `json:"total_cache,omitempty" yaml:"total_cache,omitempty"` | ||
805 | InactiveAnon uint64 `json:"inactive_anon,omitempty" yaml:"inactive_anon,omitempty"` | ||
806 | ActiveFile uint64 `json:"active_file,omitempty" yaml:"active_file,omitempty"` | ||
807 | Pgfault uint64 `json:"pgfault,omitempty" yaml:"pgfault,omitempty"` | ||
808 | InactiveFile uint64 `json:"inactive_file,omitempty" yaml:"inactive_file,omitempty"` | ||
809 | TotalPgpgin uint64 `json:"total_pgpgin,omitempty" yaml:"total_pgpgin,omitempty"` | ||
810 | HierarchicalMemswLimit uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty"` | ||
811 | Swap uint64 `json:"swap,omitempty" yaml:"swap,omitempty"` | ||
812 | } `json:"stats,omitempty" yaml:"stats,omitempty"` | ||
813 | MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty"` | ||
814 | Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty"` | ||
815 | Failcnt uint64 `json:"failcnt,omitempty" yaml:"failcnt,omitempty"` | ||
816 | Limit uint64 `json:"limit,omitempty" yaml:"limit,omitempty"` | ||
817 | } `json:"memory_stats,omitempty" yaml:"memory_stats,omitempty"` | ||
818 | BlkioStats struct { | ||
819 | IOServiceBytesRecursive []BlkioStatsEntry `json:"io_service_bytes_recursive,omitempty" yaml:"io_service_bytes_recursive,omitempty"` | ||
820 | IOServicedRecursive []BlkioStatsEntry `json:"io_serviced_recursive,omitempty" yaml:"io_serviced_recursive,omitempty"` | ||
821 | IOQueueRecursive []BlkioStatsEntry `json:"io_queue_recursive,omitempty" yaml:"io_queue_recursive,omitempty"` | ||
822 | IOServiceTimeRecursive []BlkioStatsEntry `json:"io_service_time_recursive,omitempty" yaml:"io_service_time_recursive,omitempty"` | ||
823 | IOWaitTimeRecursive []BlkioStatsEntry `json:"io_wait_time_recursive,omitempty" yaml:"io_wait_time_recursive,omitempty"` | ||
824 | IOMergedRecursive []BlkioStatsEntry `json:"io_merged_recursive,omitempty" yaml:"io_merged_recursive,omitempty"` | ||
825 | IOTimeRecursive []BlkioStatsEntry `json:"io_time_recursive,omitempty" yaml:"io_time_recursive,omitempty"` | ||
826 | SectorsRecursive []BlkioStatsEntry `json:"sectors_recursive,omitempty" yaml:"sectors_recursive,omitempty"` | ||
827 | } `json:"blkio_stats,omitempty" yaml:"blkio_stats,omitempty"` | ||
828 | CPUStats CPUStats `json:"cpu_stats,omitempty" yaml:"cpu_stats,omitempty"` | ||
829 | PreCPUStats CPUStats `json:"precpu_stats,omitempty"` | ||
830 | } | ||
831 | |||
832 | // NetworkStats is a stats entry for network stats | ||
833 | type NetworkStats struct { | ||
834 | RxDropped uint64 `json:"rx_dropped,omitempty" yaml:"rx_dropped,omitempty"` | ||
835 | RxBytes uint64 `json:"rx_bytes,omitempty" yaml:"rx_bytes,omitempty"` | ||
836 | RxErrors uint64 `json:"rx_errors,omitempty" yaml:"rx_errors,omitempty"` | ||
837 | TxPackets uint64 `json:"tx_packets,omitempty" yaml:"tx_packets,omitempty"` | ||
838 | TxDropped uint64 `json:"tx_dropped,omitempty" yaml:"tx_dropped,omitempty"` | ||
839 | RxPackets uint64 `json:"rx_packets,omitempty" yaml:"rx_packets,omitempty"` | ||
840 | TxErrors uint64 `json:"tx_errors,omitempty" yaml:"tx_errors,omitempty"` | ||
841 | TxBytes uint64 `json:"tx_bytes,omitempty" yaml:"tx_bytes,omitempty"` | ||
842 | } | ||
843 | |||
844 | // CPUStats is a stats entry for cpu stats | ||
845 | type CPUStats struct { | ||
846 | CPUUsage struct { | ||
847 | PercpuUsage []uint64 `json:"percpu_usage,omitempty" yaml:"percpu_usage,omitempty"` | ||
848 | UsageInUsermode uint64 `json:"usage_in_usermode,omitempty" yaml:"usage_in_usermode,omitempty"` | ||
849 | TotalUsage uint64 `json:"total_usage,omitempty" yaml:"total_usage,omitempty"` | ||
850 | UsageInKernelmode uint64 `json:"usage_in_kernelmode,omitempty" yaml:"usage_in_kernelmode,omitempty"` | ||
851 | } `json:"cpu_usage,omitempty" yaml:"cpu_usage,omitempty"` | ||
852 | SystemCPUUsage uint64 `json:"system_cpu_usage,omitempty" yaml:"system_cpu_usage,omitempty"` | ||
853 | ThrottlingData struct { | ||
854 | Periods uint64 `json:"periods,omitempty"` | ||
855 | ThrottledPeriods uint64 `json:"throttled_periods,omitempty"` | ||
856 | ThrottledTime uint64 `json:"throttled_time,omitempty"` | ||
857 | } `json:"throttling_data,omitempty" yaml:"throttling_data,omitempty"` | ||
858 | } | ||
859 | |||
860 | // BlkioStatsEntry is a stats entry for blkio_stats | ||
861 | type BlkioStatsEntry struct { | ||
862 | Major uint64 `json:"major,omitempty" yaml:"major,omitempty"` | ||
863 | Minor uint64 `json:"minor,omitempty" yaml:"minor,omitempty"` | ||
864 | Op string `json:"op,omitempty" yaml:"op,omitempty"` | ||
865 | Value uint64 `json:"value,omitempty" yaml:"value,omitempty"` | ||
866 | } | ||
867 | |||
868 | // StatsOptions specify parameters to the Stats function. | ||
869 | // | ||
870 | // See https://goo.gl/GNmLHb for more details. | ||
871 | type StatsOptions struct { | ||
872 | ID string | ||
873 | Stats chan<- *Stats | ||
874 | Stream bool | ||
875 | // A flag that enables stopping the stats operation | ||
876 | Done <-chan bool | ||
877 | // Initial connection timeout | ||
878 | Timeout time.Duration | ||
879 | // Timeout with no data is received, it's reset every time new data | ||
880 | // arrives | ||
881 | InactivityTimeout time.Duration `qs:"-"` | ||
882 | } | ||
883 | |||
884 | // Stats sends container statistics for the given container to the given channel. | ||
885 | // | ||
886 | // This function is blocking, similar to a streaming call for logs, and should be run | ||
887 | // on a separate goroutine from the caller. Note that this function will block until | ||
888 | // the given container is removed, not just exited. When finished, this function | ||
889 | // will close the given channel. Alternatively, function can be stopped by | ||
890 | // signaling on the Done channel. | ||
891 | // | ||
892 | // See https://goo.gl/GNmLHb for more details. | ||
893 | func (c *Client) Stats(opts StatsOptions) (retErr error) { | ||
894 | errC := make(chan error, 1) | ||
895 | readCloser, writeCloser := io.Pipe() | ||
896 | |||
897 | defer func() { | ||
898 | close(opts.Stats) | ||
899 | |||
900 | select { | ||
901 | case err := <-errC: | ||
902 | if err != nil && retErr == nil { | ||
903 | retErr = err | ||
904 | } | ||
905 | default: | ||
906 | // No errors | ||
907 | } | ||
908 | |||
909 | if err := readCloser.Close(); err != nil && retErr == nil { | ||
910 | retErr = err | ||
911 | } | ||
912 | }() | ||
913 | |||
914 | go func() { | ||
915 | err := c.stream("GET", fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{ | ||
916 | rawJSONStream: true, | ||
917 | useJSONDecoder: true, | ||
918 | stdout: writeCloser, | ||
919 | timeout: opts.Timeout, | ||
920 | inactivityTimeout: opts.InactivityTimeout, | ||
921 | }) | ||
922 | if err != nil { | ||
923 | dockerError, ok := err.(*Error) | ||
924 | if ok { | ||
925 | if dockerError.Status == http.StatusNotFound { | ||
926 | err = &NoSuchContainer{ID: opts.ID} | ||
927 | } | ||
928 | } | ||
929 | } | ||
930 | if closeErr := writeCloser.Close(); closeErr != nil && err == nil { | ||
931 | err = closeErr | ||
932 | } | ||
933 | errC <- err | ||
934 | close(errC) | ||
935 | }() | ||
936 | |||
937 | quit := make(chan struct{}) | ||
938 | defer close(quit) | ||
939 | go func() { | ||
940 | // block here waiting for the signal to stop function | ||
941 | select { | ||
942 | case <-opts.Done: | ||
943 | readCloser.Close() | ||
944 | case <-quit: | ||
945 | return | ||
946 | } | ||
947 | }() | ||
948 | |||
949 | decoder := json.NewDecoder(readCloser) | ||
950 | stats := new(Stats) | ||
951 | for err := decoder.Decode(stats); err != io.EOF; err = decoder.Decode(stats) { | ||
952 | if err != nil { | ||
953 | return err | ||
954 | } | ||
955 | opts.Stats <- stats | ||
956 | stats = new(Stats) | ||
957 | } | ||
958 | return nil | ||
959 | } | ||
960 | |||
961 | // KillContainerOptions represents the set of options that can be used in a | ||
962 | // call to KillContainer. | ||
963 | // | ||
964 | // See https://goo.gl/hkS9i8 for more details. | ||
965 | type KillContainerOptions struct { | ||
966 | // The ID of the container. | ||
967 | ID string `qs:"-"` | ||
968 | |||
969 | // The signal to send to the container. When omitted, Docker server | ||
970 | // will assume SIGKILL. | ||
971 | Signal Signal | ||
972 | } | ||
973 | |||
974 | // KillContainer sends a signal to a container, returning an error in case of | ||
975 | // failure. | ||
976 | // | ||
977 | // See https://goo.gl/hkS9i8 for more details. | ||
978 | func (c *Client) KillContainer(opts KillContainerOptions) error { | ||
979 | path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts) | ||
980 | resp, err := c.do("POST", path, doOptions{}) | ||
981 | if err != nil { | ||
982 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
983 | return &NoSuchContainer{ID: opts.ID} | ||
984 | } | ||
985 | return err | ||
986 | } | ||
987 | resp.Body.Close() | ||
988 | return nil | ||
989 | } | ||
990 | |||
991 | // RemoveContainerOptions encapsulates options to remove a container. | ||
992 | // | ||
993 | // See https://goo.gl/RQyX62 for more details. | ||
994 | type RemoveContainerOptions struct { | ||
995 | // The ID of the container. | ||
996 | ID string `qs:"-"` | ||
997 | |||
998 | // A flag that indicates whether Docker should remove the volumes | ||
999 | // associated to the container. | ||
1000 | RemoveVolumes bool `qs:"v"` | ||
1001 | |||
1002 | // A flag that indicates whether Docker should remove the container | ||
1003 | // even if it is currently running. | ||
1004 | Force bool | ||
1005 | } | ||
1006 | |||
1007 | // RemoveContainer removes a container, returning an error in case of failure. | ||
1008 | // | ||
1009 | // See https://goo.gl/RQyX62 for more details. | ||
1010 | func (c *Client) RemoveContainer(opts RemoveContainerOptions) error { | ||
1011 | path := "/containers/" + opts.ID + "?" + queryString(opts) | ||
1012 | resp, err := c.do("DELETE", path, doOptions{}) | ||
1013 | if err != nil { | ||
1014 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
1015 | return &NoSuchContainer{ID: opts.ID} | ||
1016 | } | ||
1017 | return err | ||
1018 | } | ||
1019 | resp.Body.Close() | ||
1020 | return nil | ||
1021 | } | ||
1022 | |||
1023 | // UploadToContainerOptions is the set of options that can be used when | ||
1024 | // uploading an archive into a container. | ||
1025 | // | ||
1026 | // See https://goo.gl/Ss97HW for more details. | ||
1027 | type UploadToContainerOptions struct { | ||
1028 | InputStream io.Reader `json:"-" qs:"-"` | ||
1029 | Path string `qs:"path"` | ||
1030 | NoOverwriteDirNonDir bool `qs:"noOverwriteDirNonDir"` | ||
1031 | } | ||
1032 | |||
1033 | // UploadToContainer uploads a tar archive to be extracted to a path in the | ||
1034 | // filesystem of the container. | ||
1035 | // | ||
1036 | // See https://goo.gl/Ss97HW for more details. | ||
1037 | func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) error { | ||
1038 | url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts) | ||
1039 | |||
1040 | return c.stream("PUT", url, streamOptions{ | ||
1041 | in: opts.InputStream, | ||
1042 | }) | ||
1043 | } | ||
1044 | |||
1045 | // DownloadFromContainerOptions is the set of options that can be used when | ||
1046 | // downloading resources from a container. | ||
1047 | // | ||
1048 | // See https://goo.gl/KnZJDX for more details. | ||
1049 | type DownloadFromContainerOptions struct { | ||
1050 | OutputStream io.Writer `json:"-" qs:"-"` | ||
1051 | Path string `qs:"path"` | ||
1052 | InactivityTimeout time.Duration `qs:"-"` | ||
1053 | } | ||
1054 | |||
1055 | // DownloadFromContainer downloads a tar archive of files or folders in a container. | ||
1056 | // | ||
1057 | // See https://goo.gl/KnZJDX for more details. | ||
1058 | func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOptions) error { | ||
1059 | url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts) | ||
1060 | |||
1061 | return c.stream("GET", url, streamOptions{ | ||
1062 | setRawTerminal: true, | ||
1063 | stdout: opts.OutputStream, | ||
1064 | inactivityTimeout: opts.InactivityTimeout, | ||
1065 | }) | ||
1066 | } | ||
1067 | |||
1068 | // CopyFromContainerOptions has been DEPRECATED, please use DownloadFromContainerOptions along with DownloadFromContainer. | ||
1069 | // | ||
1070 | // See https://goo.gl/R2jevW for more details. | ||
1071 | type CopyFromContainerOptions struct { | ||
1072 | OutputStream io.Writer `json:"-"` | ||
1073 | Container string `json:"-"` | ||
1074 | Resource string | ||
1075 | } | ||
1076 | |||
1077 | // CopyFromContainer has been DEPRECATED, please use DownloadFromContainerOptions along with DownloadFromContainer. | ||
1078 | // | ||
1079 | // See https://goo.gl/R2jevW for more details. | ||
1080 | func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error { | ||
1081 | if opts.Container == "" { | ||
1082 | return &NoSuchContainer{ID: opts.Container} | ||
1083 | } | ||
1084 | url := fmt.Sprintf("/containers/%s/copy", opts.Container) | ||
1085 | resp, err := c.do("POST", url, doOptions{data: opts}) | ||
1086 | if err != nil { | ||
1087 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
1088 | return &NoSuchContainer{ID: opts.Container} | ||
1089 | } | ||
1090 | return err | ||
1091 | } | ||
1092 | defer resp.Body.Close() | ||
1093 | _, err = io.Copy(opts.OutputStream, resp.Body) | ||
1094 | return err | ||
1095 | } | ||
1096 | |||
1097 | // WaitContainer blocks until the given container stops, return the exit code | ||
1098 | // of the container status. | ||
1099 | // | ||
1100 | // See https://goo.gl/Gc1rge for more details. | ||
1101 | func (c *Client) WaitContainer(id string) (int, error) { | ||
1102 | resp, err := c.do("POST", "/containers/"+id+"/wait", doOptions{}) | ||
1103 | if err != nil { | ||
1104 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
1105 | return 0, &NoSuchContainer{ID: id} | ||
1106 | } | ||
1107 | return 0, err | ||
1108 | } | ||
1109 | defer resp.Body.Close() | ||
1110 | var r struct{ StatusCode int } | ||
1111 | if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { | ||
1112 | return 0, err | ||
1113 | } | ||
1114 | return r.StatusCode, nil | ||
1115 | } | ||
1116 | |||
1117 | // CommitContainerOptions aggregates parameters to the CommitContainer method. | ||
1118 | // | ||
1119 | // See https://goo.gl/mqfoCw for more details. | ||
1120 | type CommitContainerOptions struct { | ||
1121 | Container string | ||
1122 | Repository string `qs:"repo"` | ||
1123 | Tag string | ||
1124 | Message string `qs:"comment"` | ||
1125 | Author string | ||
1126 | Run *Config `qs:"-"` | ||
1127 | } | ||
1128 | |||
1129 | // CommitContainer creates a new image from a container's changes. | ||
1130 | // | ||
1131 | // See https://goo.gl/mqfoCw for more details. | ||
1132 | func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) { | ||
1133 | path := "/commit?" + queryString(opts) | ||
1134 | resp, err := c.do("POST", path, doOptions{data: opts.Run}) | ||
1135 | if err != nil { | ||
1136 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
1137 | return nil, &NoSuchContainer{ID: opts.Container} | ||
1138 | } | ||
1139 | return nil, err | ||
1140 | } | ||
1141 | defer resp.Body.Close() | ||
1142 | var image Image | ||
1143 | if err := json.NewDecoder(resp.Body).Decode(&image); err != nil { | ||
1144 | return nil, err | ||
1145 | } | ||
1146 | return &image, nil | ||
1147 | } | ||
1148 | |||
1149 | // AttachToContainerOptions is the set of options that can be used when | ||
1150 | // attaching to a container. | ||
1151 | // | ||
1152 | // See https://goo.gl/NKpkFk for more details. | ||
1153 | type AttachToContainerOptions struct { | ||
1154 | Container string `qs:"-"` | ||
1155 | InputStream io.Reader `qs:"-"` | ||
1156 | OutputStream io.Writer `qs:"-"` | ||
1157 | ErrorStream io.Writer `qs:"-"` | ||
1158 | |||
1159 | // Get container logs, sending it to OutputStream. | ||
1160 | Logs bool | ||
1161 | |||
1162 | // Stream the response? | ||
1163 | Stream bool | ||
1164 | |||
1165 | // Attach to stdin, and use InputStream. | ||
1166 | Stdin bool | ||
1167 | |||
1168 | // Attach to stdout, and use OutputStream. | ||
1169 | Stdout bool | ||
1170 | |||
1171 | // Attach to stderr, and use ErrorStream. | ||
1172 | Stderr bool | ||
1173 | |||
1174 | // If set, after a successful connect, a sentinel will be sent and then the | ||
1175 | // client will block on receive before continuing. | ||
1176 | // | ||
1177 | // It must be an unbuffered channel. Using a buffered channel can lead | ||
1178 | // to unexpected behavior. | ||
1179 | Success chan struct{} | ||
1180 | |||
1181 | // Use raw terminal? Usually true when the container contains a TTY. | ||
1182 | RawTerminal bool `qs:"-"` | ||
1183 | } | ||
1184 | |||
1185 | // AttachToContainer attaches to a container, using the given options. | ||
1186 | // | ||
1187 | // See https://goo.gl/NKpkFk for more details. | ||
1188 | func (c *Client) AttachToContainer(opts AttachToContainerOptions) error { | ||
1189 | cw, err := c.AttachToContainerNonBlocking(opts) | ||
1190 | if err != nil { | ||
1191 | return err | ||
1192 | } | ||
1193 | return cw.Wait() | ||
1194 | } | ||
1195 | |||
1196 | // AttachToContainerNonBlocking attaches to a container, using the given options. | ||
1197 | // This function does not block. | ||
1198 | // | ||
1199 | // See https://goo.gl/NKpkFk for more details. | ||
1200 | func (c *Client) AttachToContainerNonBlocking(opts AttachToContainerOptions) (CloseWaiter, error) { | ||
1201 | if opts.Container == "" { | ||
1202 | return nil, &NoSuchContainer{ID: opts.Container} | ||
1203 | } | ||
1204 | path := "/containers/" + opts.Container + "/attach?" + queryString(opts) | ||
1205 | return c.hijack("POST", path, hijackOptions{ | ||
1206 | success: opts.Success, | ||
1207 | setRawTerminal: opts.RawTerminal, | ||
1208 | in: opts.InputStream, | ||
1209 | stdout: opts.OutputStream, | ||
1210 | stderr: opts.ErrorStream, | ||
1211 | }) | ||
1212 | } | ||
1213 | |||
1214 | // LogsOptions represents the set of options used when getting logs from a | ||
1215 | // container. | ||
1216 | // | ||
1217 | // See https://goo.gl/yl8PGm for more details. | ||
1218 | type LogsOptions struct { | ||
1219 | Container string `qs:"-"` | ||
1220 | OutputStream io.Writer `qs:"-"` | ||
1221 | ErrorStream io.Writer `qs:"-"` | ||
1222 | InactivityTimeout time.Duration `qs:"-"` | ||
1223 | Follow bool | ||
1224 | Stdout bool | ||
1225 | Stderr bool | ||
1226 | Since int64 | ||
1227 | Timestamps bool | ||
1228 | Tail string | ||
1229 | |||
1230 | // Use raw terminal? Usually true when the container contains a TTY. | ||
1231 | RawTerminal bool `qs:"-"` | ||
1232 | } | ||
1233 | |||
1234 | // Logs gets stdout and stderr logs from the specified container. | ||
1235 | // | ||
1236 | // See https://goo.gl/yl8PGm for more details. | ||
1237 | func (c *Client) Logs(opts LogsOptions) error { | ||
1238 | if opts.Container == "" { | ||
1239 | return &NoSuchContainer{ID: opts.Container} | ||
1240 | } | ||
1241 | if opts.Tail == "" { | ||
1242 | opts.Tail = "all" | ||
1243 | } | ||
1244 | path := "/containers/" + opts.Container + "/logs?" + queryString(opts) | ||
1245 | return c.stream("GET", path, streamOptions{ | ||
1246 | setRawTerminal: opts.RawTerminal, | ||
1247 | stdout: opts.OutputStream, | ||
1248 | stderr: opts.ErrorStream, | ||
1249 | inactivityTimeout: opts.InactivityTimeout, | ||
1250 | }) | ||
1251 | } | ||
1252 | |||
1253 | // ResizeContainerTTY resizes the terminal to the given height and width. | ||
1254 | // | ||
1255 | // See https://goo.gl/xERhCc for more details. | ||
1256 | func (c *Client) ResizeContainerTTY(id string, height, width int) error { | ||
1257 | params := make(url.Values) | ||
1258 | params.Set("h", strconv.Itoa(height)) | ||
1259 | params.Set("w", strconv.Itoa(width)) | ||
1260 | resp, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), doOptions{}) | ||
1261 | if err != nil { | ||
1262 | return err | ||
1263 | } | ||
1264 | resp.Body.Close() | ||
1265 | return nil | ||
1266 | } | ||
1267 | |||
1268 | // ExportContainerOptions is the set of parameters to the ExportContainer | ||
1269 | // method. | ||
1270 | // | ||
1271 | // See https://goo.gl/dOkTyk for more details. | ||
1272 | type ExportContainerOptions struct { | ||
1273 | ID string | ||
1274 | OutputStream io.Writer | ||
1275 | InactivityTimeout time.Duration `qs:"-"` | ||
1276 | } | ||
1277 | |||
1278 | // ExportContainer export the contents of container id as tar archive | ||
1279 | // and prints the exported contents to stdout. | ||
1280 | // | ||
1281 | // See https://goo.gl/dOkTyk for more details. | ||
1282 | func (c *Client) ExportContainer(opts ExportContainerOptions) error { | ||
1283 | if opts.ID == "" { | ||
1284 | return &NoSuchContainer{ID: opts.ID} | ||
1285 | } | ||
1286 | url := fmt.Sprintf("/containers/%s/export", opts.ID) | ||
1287 | return c.stream("GET", url, streamOptions{ | ||
1288 | setRawTerminal: true, | ||
1289 | stdout: opts.OutputStream, | ||
1290 | inactivityTimeout: opts.InactivityTimeout, | ||
1291 | }) | ||
1292 | } | ||
1293 | |||
1294 | // NoSuchContainer is the error returned when a given container does not exist. | ||
1295 | type NoSuchContainer struct { | ||
1296 | ID string | ||
1297 | Err error | ||
1298 | } | ||
1299 | |||
1300 | func (err *NoSuchContainer) Error() string { | ||
1301 | if err.Err != nil { | ||
1302 | return err.Err.Error() | ||
1303 | } | ||
1304 | return "No such container: " + err.ID | ||
1305 | } | ||
1306 | |||
1307 | // ContainerAlreadyRunning is the error returned when a given container is | ||
1308 | // already running. | ||
1309 | type ContainerAlreadyRunning struct { | ||
1310 | ID string | ||
1311 | } | ||
1312 | |||
1313 | func (err *ContainerAlreadyRunning) Error() string { | ||
1314 | return "Container already running: " + err.ID | ||
1315 | } | ||
1316 | |||
1317 | // ContainerNotRunning is the error returned when a given container is not | ||
1318 | // running. | ||
1319 | type ContainerNotRunning struct { | ||
1320 | ID string | ||
1321 | } | ||
1322 | |||
1323 | func (err *ContainerNotRunning) Error() string { | ||
1324 | return "Container not running: " + err.ID | ||
1325 | } | ||