]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/fsouza/go-dockerclient/container.go
provider: Ensured Go 1.11 in TravisCI and README
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / fsouza / go-dockerclient / container.go
CommitLineData
9b12e4fe
JC
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
5package docker
6
7import (
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.
23var 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.
28type 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
38type 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.
46type 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.
58type 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.
76type 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.
83func (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.
99type Port string
100
101// Port returns the number of the port.
102func (p Port) Port() string {
103 return strings.Split(string(p), "/")[0]
104}
105
106// Proto returns the name of the protocol.
107func (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.
116type 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
132func (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
164func (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
188type 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
195type PortMapping map[string]string
196
197// ContainerNetwork represents the networking settings of a container per network.
198type 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
211type 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
234func (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
259func 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
270type 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.
311type 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.
321type 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.
328type 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
335type 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
346type 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.
353type 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.
392type 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.
409func (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.
421type 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.
432func (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.
444func (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.
464func (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.
484type 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.
494func (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
535type 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
548type 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.
555func 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.
561func 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.
567func NeverRestart() RestartPolicy {
568 return RestartPolicy{Name: "no"}
569}
570
571// Device represents a device mapping between the Docker host and the
572// container.
573type 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.
583type 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.
592type 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
599type 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.
651func (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.
671func (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.
691func (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.
707func (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.
723func (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.
740type 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.
748func (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.
772type 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
833type 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
845type 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
861type 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.
871type 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.
893func (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.
965type 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.
978func (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.
994type 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.
1010func (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.
1027type 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.
1037func (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.
1049type 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.
1058func (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.
1071type 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.
1080func (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.
1101func (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.
1120type 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.
1132func (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.
1153type 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.
1188func (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.
1200func (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.
1218type 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.
1237func (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.
1256func (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.
1272type 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.
1282func (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.
1295type NoSuchContainer struct {
1296 ID string
1297 Err error
1298}
1299
1300func (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.
1309type ContainerAlreadyRunning struct {
1310 ID string
1311}
1312
1313func (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.
1319type ContainerNotRunning struct {
1320 ID string
1321}
1322
1323func (err *ContainerNotRunning) Error() string {
1324 return "Container not running: " + err.ID
1325}