12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325 |
- // Copyright 2015 go-dockerclient authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package docker
- import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
- "github.com/fsouza/go-dockerclient/external/github.com/docker/go-units"
- )
- // ErrContainerAlreadyExists is the error returned by CreateContainer when the
- // container already exists.
- var ErrContainerAlreadyExists = errors.New("container already exists")
- // ListContainersOptions specify parameters to the ListContainers function.
- //
- // See https://goo.gl/47a6tO for more details.
- type ListContainersOptions struct {
- All bool
- Size bool
- Limit int
- Since string
- Before string
- Filters map[string][]string
- }
- // APIPort is a type that represents a port mapping returned by the Docker API
- type APIPort struct {
- PrivatePort int64 `json:"PrivatePort,omitempty" yaml:"PrivatePort,omitempty"`
- PublicPort int64 `json:"PublicPort,omitempty" yaml:"PublicPort,omitempty"`
- Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
- IP string `json:"IP,omitempty" yaml:"IP,omitempty"`
- }
- // APIMount represents a mount point for a container.
- type APIMount struct {
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- Source string `json:"Source,omitempty" yaml:"Source,omitempty"`
- Destination string `json:"Destination,omitempty" yaml:"Destination,omitempty"`
- Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"`
- Mode string `json:"Mode,omitempty" yaml:"Mode,omitempty"`
- RW bool `json:"RW,omitempty" yaml:"RW,omitempty"`
- Propogation string `json:"Propogation,omitempty" yaml:"Propogation,omitempty"`
- }
- // APIContainers represents each container in the list returned by
- // ListContainers.
- type APIContainers struct {
- ID string `json:"Id" yaml:"Id"`
- Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
- Command string `json:"Command,omitempty" yaml:"Command,omitempty"`
- Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
- State string `json:"State,omitempty" yaml:"State,omitempty"`
- Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
- Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"`
- SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
- SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
- Names []string `json:"Names,omitempty" yaml:"Names,omitempty"`
- Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
- Networks NetworkList `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"`
- Mounts []APIMount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"`
- }
- // NetworkList encapsulates a map of networks, as returned by the Docker API in
- // ListContainers.
- type NetworkList struct {
- Networks map[string]ContainerNetwork `json:"Networks" yaml:"Networks,omitempty"`
- }
- // ListContainers returns a slice of containers matching the given criteria.
- //
- // See https://goo.gl/47a6tO for more details.
- func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
- path := "/containers/json?" + queryString(opts)
- resp, err := c.do("GET", path, doOptions{})
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- var containers []APIContainers
- if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil {
- return nil, err
- }
- return containers, nil
- }
- // Port represents the port number and the protocol, in the form
- // <number>/<protocol>. For example: 80/tcp.
- type Port string
- // Port returns the number of the port.
- func (p Port) Port() string {
- return strings.Split(string(p), "/")[0]
- }
- // Proto returns the name of the protocol.
- func (p Port) Proto() string {
- parts := strings.Split(string(p), "/")
- if len(parts) == 1 {
- return "tcp"
- }
- return parts[1]
- }
- // State represents the state of a container.
- type State struct {
- Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
- Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
- Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"`
- Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"`
- OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty"`
- RemovalInProgress bool `json:"RemovalInProgress,omitempty" yaml:"RemovalInProgress,omitempty"`
- Dead bool `json:"Dead,omitempty" yaml:"Dead,omitempty"`
- Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"`
- ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
- Error string `json:"Error,omitempty" yaml:"Error,omitempty"`
- StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"`
- FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"`
- }
- // String returns a human-readable description of the state
- func (s *State) String() string {
- if s.Running {
- if s.Paused {
- return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
- }
- if s.Restarting {
- return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
- }
- return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
- }
- if s.RemovalInProgress {
- return "Removal In Progress"
- }
- if s.Dead {
- return "Dead"
- }
- if s.StartedAt.IsZero() {
- return "Created"
- }
- if s.FinishedAt.IsZero() {
- return ""
- }
- return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
- }
- // StateString returns a single string to describe state
- func (s *State) StateString() string {
- if s.Running {
- if s.Paused {
- return "paused"
- }
- if s.Restarting {
- return "restarting"
- }
- return "running"
- }
- if s.Dead {
- return "dead"
- }
- if s.StartedAt.IsZero() {
- return "created"
- }
- return "exited"
- }
- // PortBinding represents the host/container port mapping as returned in the
- // `docker inspect` json
- type PortBinding struct {
- HostIP string `json:"HostIP,omitempty" yaml:"HostIP,omitempty"`
- HostPort string `json:"HostPort,omitempty" yaml:"HostPort,omitempty"`
- }
- // PortMapping represents a deprecated field in the `docker inspect` output,
- // and its value as found in NetworkSettings should always be nil
- type PortMapping map[string]string
- // ContainerNetwork represents the networking settings of a container per network.
- type ContainerNetwork struct {
- MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"`
- GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty"`
- GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty"`
- IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty"`
- IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty"`
- IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"`
- Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"`
- EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"`
- NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"`
- }
- // NetworkSettings contains network-related information about a container
- type NetworkSettings struct {
- Networks map[string]ContainerNetwork `json:"Networks,omitempty" yaml:"Networks,omitempty"`
- IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"`
- IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty"`
- MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"`
- Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"`
- Bridge string `json:"Bridge,omitempty" yaml:"Bridge,omitempty"`
- PortMapping map[string]PortMapping `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty"`
- Ports map[Port][]PortBinding `json:"Ports,omitempty" yaml:"Ports,omitempty"`
- NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"`
- EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"`
- SandboxKey string `json:"SandboxKey,omitempty" yaml:"SandboxKey,omitempty"`
- GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty"`
- GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty"`
- IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty"`
- LinkLocalIPv6Address string `json:"LinkLocalIPv6Address,omitempty" yaml:"LinkLocalIPv6Address,omitempty"`
- LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen,omitempty" yaml:"LinkLocalIPv6PrefixLen,omitempty"`
- SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty" yaml:"SecondaryIPAddresses,omitempty"`
- SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty" yaml:"SecondaryIPv6Addresses,omitempty"`
- }
- // PortMappingAPI translates the port mappings as contained in NetworkSettings
- // into the format in which they would appear when returned by the API
- func (settings *NetworkSettings) PortMappingAPI() []APIPort {
- var mapping []APIPort
- for port, bindings := range settings.Ports {
- p, _ := parsePort(port.Port())
- if len(bindings) == 0 {
- mapping = append(mapping, APIPort{
- PrivatePort: int64(p),
- Type: port.Proto(),
- })
- continue
- }
- for _, binding := range bindings {
- p, _ := parsePort(port.Port())
- h, _ := parsePort(binding.HostPort)
- mapping = append(mapping, APIPort{
- PrivatePort: int64(p),
- PublicPort: int64(h),
- Type: port.Proto(),
- IP: binding.HostIP,
- })
- }
- }
- return mapping
- }
- func parsePort(rawPort string) (int, error) {
- port, err := strconv.ParseUint(rawPort, 10, 16)
- if err != nil {
- return 0, err
- }
- return int(port), nil
- }
- // Config is the list of configuration options used when creating a container.
- // Config does not contain the options that are specific to starting a container on a
- // given host. Those are contained in HostConfig
- type Config struct {
- Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty"`
- Domainname string `json:"Domainname,omitempty" yaml:"Domainname,omitempty"`
- User string `json:"User,omitempty" yaml:"User,omitempty"`
- Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
- MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
- MemoryReservation int64 `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty"`
- KernelMemory int64 `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty"`
- PidsLimit int64 `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty"`
- CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
- CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
- AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
- AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
- AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"`
- PortSpecs []string `json:"PortSpecs,omitempty" yaml:"PortSpecs,omitempty"`
- ExposedPorts map[Port]struct{} `json:"ExposedPorts,omitempty" yaml:"ExposedPorts,omitempty"`
- StopSignal string `json:"StopSignal,omitempty" yaml:"StopSignal,omitempty"`
- Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"`
- OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"`
- StdinOnce bool `json:"StdinOnce,omitempty" yaml:"StdinOnce,omitempty"`
- Env []string `json:"Env,omitempty" yaml:"Env,omitempty"`
- Cmd []string `json:"Cmd" yaml:"Cmd"`
- DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.9 and below only
- Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
- Volumes map[string]struct{} `json:"Volumes,omitempty" yaml:"Volumes,omitempty"`
- VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
- VolumesFrom string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
- WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty"`
- MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"`
- Entrypoint []string `json:"Entrypoint" yaml:"Entrypoint"`
- NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"`
- SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty"`
- OnBuild []string `json:"OnBuild,omitempty" yaml:"OnBuild,omitempty"`
- Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"`
- Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
- }
- // Mount represents a mount point in the container.
- //
- // It has been added in the version 1.20 of the Docker API, available since
- // Docker 1.8.
- type Mount struct {
- Name string
- Source string
- Destination string
- Driver string
- Mode string
- RW bool
- }
- // LogConfig defines the log driver type and the configuration for it.
- type LogConfig struct {
- Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
- Config map[string]string `json:"Config,omitempty" yaml:"Config,omitempty"`
- }
- // ULimit defines system-wide resource limitations
- // 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.
- type ULimit struct {
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- Soft int64 `json:"Soft,omitempty" yaml:"Soft,omitempty"`
- Hard int64 `json:"Hard,omitempty" yaml:"Hard,omitempty"`
- }
- // SwarmNode containers information about which Swarm node the container is on
- type SwarmNode struct {
- ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
- IP string `json:"IP,omitempty" yaml:"IP,omitempty"`
- Addr string `json:"Addr,omitempty" yaml:"Addr,omitempty"`
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- CPUs int64 `json:"CPUs,omitempty" yaml:"CPUs,omitempty"`
- Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
- Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
- }
- // GraphDriver contains information about the GraphDriver used by the container
- type GraphDriver struct {
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- Data map[string]string `json:"Data,omitempty" yaml:"Data,omitempty"`
- }
- // Container is the type encompasing everything about a container - its config,
- // hostconfig, etc.
- type Container struct {
- ID string `json:"Id" yaml:"Id"`
- Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"`
- Path string `json:"Path,omitempty" yaml:"Path,omitempty"`
- Args []string `json:"Args,omitempty" yaml:"Args,omitempty"`
- Config *Config `json:"Config,omitempty" yaml:"Config,omitempty"`
- State State `json:"State,omitempty" yaml:"State,omitempty"`
- Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
- Node *SwarmNode `json:"Node,omitempty" yaml:"Node,omitempty"`
- NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"`
- SysInitPath string `json:"SysInitPath,omitempty" yaml:"SysInitPath,omitempty"`
- ResolvConfPath string `json:"ResolvConfPath,omitempty" yaml:"ResolvConfPath,omitempty"`
- HostnamePath string `json:"HostnamePath,omitempty" yaml:"HostnamePath,omitempty"`
- HostsPath string `json:"HostsPath,omitempty" yaml:"HostsPath,omitempty"`
- LogPath string `json:"LogPath,omitempty" yaml:"LogPath,omitempty"`
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"`
- Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"`
- Volumes map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty"`
- VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty"`
- HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"`
- ExecIDs []string `json:"ExecIDs,omitempty" yaml:"ExecIDs,omitempty"`
- GraphDriver *GraphDriver `json:"GraphDriver,omitempty" yaml:"GraphDriver,omitempty"`
- RestartCount int `json:"RestartCount,omitempty" yaml:"RestartCount,omitempty"`
- AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty"`
- }
- // UpdateContainerOptions specify parameters to the UpdateContainer function.
- //
- // See https://goo.gl/Y6fXUy for more details.
- type UpdateContainerOptions struct {
- BlkioWeight int `json:"BlkioWeight"`
- CPUShares int `json:"CpuShares"`
- CPUPeriod int `json:"CpuPeriod"`
- CPUQuota int `json:"CpuQuota"`
- CpusetCpus string `json:"CpusetCpus"`
- CpusetMems string `json:"CpusetMems"`
- Memory int `json:"Memory"`
- MemorySwap int `json:"MemorySwap"`
- MemoryReservation int `json:"MemoryReservation"`
- KernelMemory int `json:"KernelMemory"`
- RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty"`
- }
- // UpdateContainer updates the container at ID with the options
- //
- // See https://goo.gl/Y6fXUy for more details.
- func (c *Client) UpdateContainer(id string, opts UpdateContainerOptions) error {
- resp, err := c.do("POST", fmt.Sprintf("/containers/"+id+"/update"), doOptions{data: opts, forceJSON: true})
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return nil
- }
- // RenameContainerOptions specify parameters to the RenameContainer function.
- //
- // See https://goo.gl/laSOIy for more details.
- type RenameContainerOptions struct {
- // ID of container to rename
- ID string `qs:"-"`
- // New name
- Name string `json:"name,omitempty" yaml:"name,omitempty"`
- }
- // RenameContainer updates and existing containers name
- //
- // See https://goo.gl/laSOIy for more details.
- func (c *Client) RenameContainer(opts RenameContainerOptions) error {
- resp, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{})
- if err != nil {
- return err
- }
- resp.Body.Close()
- return nil
- }
- // InspectContainer returns information about a container by its ID.
- //
- // See https://goo.gl/RdIq0b for more details.
- func (c *Client) InspectContainer(id string) (*Container, error) {
- path := "/containers/" + id + "/json"
- resp, err := c.do("GET", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return nil, &NoSuchContainer{ID: id}
- }
- return nil, err
- }
- defer resp.Body.Close()
- var container Container
- if err := json.NewDecoder(resp.Body).Decode(&container); err != nil {
- return nil, err
- }
- return &container, nil
- }
- // ContainerChanges returns changes in the filesystem of the given container.
- //
- // See https://goo.gl/9GsTIF for more details.
- func (c *Client) ContainerChanges(id string) ([]Change, error) {
- path := "/containers/" + id + "/changes"
- resp, err := c.do("GET", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return nil, &NoSuchContainer{ID: id}
- }
- return nil, err
- }
- defer resp.Body.Close()
- var changes []Change
- if err := json.NewDecoder(resp.Body).Decode(&changes); err != nil {
- return nil, err
- }
- return changes, nil
- }
- // CreateContainerOptions specify parameters to the CreateContainer function.
- //
- // See https://goo.gl/WxQzrr for more details.
- type CreateContainerOptions struct {
- Name string
- Config *Config `qs:"-"`
- HostConfig *HostConfig `qs:"-"`
- }
- // CreateContainer creates a new container, returning the container instance,
- // or an error in case of failure.
- //
- // See https://goo.gl/WxQzrr for more details.
- func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
- path := "/containers/create?" + queryString(opts)
- resp, err := c.do(
- "POST",
- path,
- doOptions{
- data: struct {
- *Config
- HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"`
- }{
- opts.Config,
- opts.HostConfig,
- },
- },
- )
- if e, ok := err.(*Error); ok {
- if e.Status == http.StatusNotFound {
- return nil, ErrNoSuchImage
- }
- if e.Status == http.StatusConflict {
- return nil, ErrContainerAlreadyExists
- }
- }
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- var container Container
- if err := json.NewDecoder(resp.Body).Decode(&container); err != nil {
- return nil, err
- }
- container.Name = opts.Name
- return &container, nil
- }
- // KeyValuePair is a type for generic key/value pairs as used in the Lxc
- // configuration
- type KeyValuePair struct {
- Key string `json:"Key,omitempty" yaml:"Key,omitempty"`
- Value string `json:"Value,omitempty" yaml:"Value,omitempty"`
- }
- // RestartPolicy represents the policy for automatically restarting a container.
- //
- // Possible values are:
- //
- // - always: the docker daemon will always restart the container
- // - on-failure: the docker daemon will restart the container on failures, at
- // most MaximumRetryCount times
- // - no: the docker daemon will not restart the container automatically
- type RestartPolicy struct {
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- MaximumRetryCount int `json:"MaximumRetryCount,omitempty" yaml:"MaximumRetryCount,omitempty"`
- }
- // AlwaysRestart returns a restart policy that tells the Docker daemon to
- // always restart the container.
- func AlwaysRestart() RestartPolicy {
- return RestartPolicy{Name: "always"}
- }
- // RestartOnFailure returns a restart policy that tells the Docker daemon to
- // restart the container on failures, trying at most maxRetry times.
- func RestartOnFailure(maxRetry int) RestartPolicy {
- return RestartPolicy{Name: "on-failure", MaximumRetryCount: maxRetry}
- }
- // NeverRestart returns a restart policy that tells the Docker daemon to never
- // restart the container on failures.
- func NeverRestart() RestartPolicy {
- return RestartPolicy{Name: "no"}
- }
- // Device represents a device mapping between the Docker host and the
- // container.
- type Device struct {
- PathOnHost string `json:"PathOnHost,omitempty" yaml:"PathOnHost,omitempty"`
- PathInContainer string `json:"PathInContainer,omitempty" yaml:"PathInContainer,omitempty"`
- CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty"`
- }
- // BlockWeight represents a relative device weight for an individual device inside
- // of a container
- //
- // See https://goo.gl/FSdP0H for more details.
- type BlockWeight struct {
- Path string `json:"Path,omitempty"`
- Weight string `json:"Weight,omitempty"`
- }
- // BlockLimit represents a read/write limit in IOPS or Bandwidth for a device
- // inside of a container
- //
- // See https://goo.gl/FSdP0H for more details.
- type BlockLimit struct {
- Path string `json:"Path,omitempty"`
- Rate string `json:"Rate,omitempty"`
- }
- // HostConfig contains the container options related to starting a container on
- // a given host
- type HostConfig struct {
- Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
- CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
- CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
- GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty"`
- ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
- LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
- Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
- PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
- Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
- PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
- DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
- DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty"`
- DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
- ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
- VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
- UsernsMode string `json:"UsernsMode,omitempty" yaml:"UsernsMode,omitempty"`
- NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
- IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
- PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
- UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
- RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
- Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
- LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
- ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
- SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
- CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
- Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
- MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
- MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
- OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
- CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
- CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
- CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"`
- CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"`
- CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
- CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
- BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"`
- BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice"`
- BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps"`
- BlkioDeviceReadIOps []BlockLimit `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps"`
- BlkioDeviceWriteBps []BlockLimit `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps"`
- BlkioDeviceWriteIOps []BlockLimit `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps"`
- Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
- VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
- OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"`
- ShmSize int64 `json:"ShmSize,omitempty" yaml:"ShmSize,omitempty"`
- }
- // StartContainer starts a container, returning an error in case of failure.
- //
- // See https://goo.gl/MrBAJv for more details.
- func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
- path := "/containers/" + id + "/start"
- resp, err := c.do("POST", path, doOptions{data: hostConfig, forceJSON: true})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: id, Err: err}
- }
- return err
- }
- if resp.StatusCode == http.StatusNotModified {
- return &ContainerAlreadyRunning{ID: id}
- }
- resp.Body.Close()
- return nil
- }
- // StopContainer stops a container, killing it after the given timeout (in
- // seconds).
- //
- // See https://goo.gl/USqsFt for more details.
- func (c *Client) StopContainer(id string, timeout uint) error {
- path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
- resp, err := c.do("POST", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: id}
- }
- return err
- }
- if resp.StatusCode == http.StatusNotModified {
- return &ContainerNotRunning{ID: id}
- }
- resp.Body.Close()
- return nil
- }
- // RestartContainer stops a container, killing it after the given timeout (in
- // seconds), during the stop process.
- //
- // See https://goo.gl/QzsDnz for more details.
- func (c *Client) RestartContainer(id string, timeout uint) error {
- path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
- resp, err := c.do("POST", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: id}
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // PauseContainer pauses the given container.
- //
- // See https://goo.gl/OF7W9X for more details.
- func (c *Client) PauseContainer(id string) error {
- path := fmt.Sprintf("/containers/%s/pause", id)
- resp, err := c.do("POST", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: id}
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // UnpauseContainer unpauses the given container.
- //
- // See https://goo.gl/7dwyPA for more details.
- func (c *Client) UnpauseContainer(id string) error {
- path := fmt.Sprintf("/containers/%s/unpause", id)
- resp, err := c.do("POST", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: id}
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // TopResult represents the list of processes running in a container, as
- // returned by /containers/<id>/top.
- //
- // See https://goo.gl/Rb46aY for more details.
- type TopResult struct {
- Titles []string
- Processes [][]string
- }
- // TopContainer returns processes running inside a container
- //
- // See https://goo.gl/Rb46aY for more details.
- func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
- var args string
- var result TopResult
- if psArgs != "" {
- args = fmt.Sprintf("?ps_args=%s", psArgs)
- }
- path := fmt.Sprintf("/containers/%s/top%s", id, args)
- resp, err := c.do("GET", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return result, &NoSuchContainer{ID: id}
- }
- return result, err
- }
- defer resp.Body.Close()
- if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
- return result, err
- }
- return result, nil
- }
- // Stats represents container statistics, returned by /containers/<id>/stats.
- //
- // See https://goo.gl/GNmLHb for more details.
- type Stats struct {
- Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
- PidsStats struct {
- Current uint64 `json:"current,omitempty" yaml:"current,omitempty"`
- } `json:"pids_stats,omitempty" yaml:"pids_stats,omitempty"`
- Network NetworkStats `json:"network,omitempty" yaml:"network,omitempty"`
- Networks map[string]NetworkStats `json:"networks,omitempty" yaml:"networks,omitempty"`
- MemoryStats struct {
- Stats struct {
- TotalPgmafault uint64 `json:"total_pgmafault,omitempty" yaml:"total_pgmafault,omitempty"`
- Cache uint64 `json:"cache,omitempty" yaml:"cache,omitempty"`
- MappedFile uint64 `json:"mapped_file,omitempty" yaml:"mapped_file,omitempty"`
- TotalInactiveFile uint64 `json:"total_inactive_file,omitempty" yaml:"total_inactive_file,omitempty"`
- Pgpgout uint64 `json:"pgpgout,omitempty" yaml:"pgpgout,omitempty"`
- Rss uint64 `json:"rss,omitempty" yaml:"rss,omitempty"`
- TotalMappedFile uint64 `json:"total_mapped_file,omitempty" yaml:"total_mapped_file,omitempty"`
- Writeback uint64 `json:"writeback,omitempty" yaml:"writeback,omitempty"`
- Unevictable uint64 `json:"unevictable,omitempty" yaml:"unevictable,omitempty"`
- Pgpgin uint64 `json:"pgpgin,omitempty" yaml:"pgpgin,omitempty"`
- TotalUnevictable uint64 `json:"total_unevictable,omitempty" yaml:"total_unevictable,omitempty"`
- Pgmajfault uint64 `json:"pgmajfault,omitempty" yaml:"pgmajfault,omitempty"`
- TotalRss uint64 `json:"total_rss,omitempty" yaml:"total_rss,omitempty"`
- TotalRssHuge uint64 `json:"total_rss_huge,omitempty" yaml:"total_rss_huge,omitempty"`
- TotalWriteback uint64 `json:"total_writeback,omitempty" yaml:"total_writeback,omitempty"`
- TotalInactiveAnon uint64 `json:"total_inactive_anon,omitempty" yaml:"total_inactive_anon,omitempty"`
- RssHuge uint64 `json:"rss_huge,omitempty" yaml:"rss_huge,omitempty"`
- HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit,omitempty" yaml:"hierarchical_memory_limit,omitempty"`
- TotalPgfault uint64 `json:"total_pgfault,omitempty" yaml:"total_pgfault,omitempty"`
- TotalActiveFile uint64 `json:"total_active_file,omitempty" yaml:"total_active_file,omitempty"`
- ActiveAnon uint64 `json:"active_anon,omitempty" yaml:"active_anon,omitempty"`
- TotalActiveAnon uint64 `json:"total_active_anon,omitempty" yaml:"total_active_anon,omitempty"`
- TotalPgpgout uint64 `json:"total_pgpgout,omitempty" yaml:"total_pgpgout,omitempty"`
- TotalCache uint64 `json:"total_cache,omitempty" yaml:"total_cache,omitempty"`
- InactiveAnon uint64 `json:"inactive_anon,omitempty" yaml:"inactive_anon,omitempty"`
- ActiveFile uint64 `json:"active_file,omitempty" yaml:"active_file,omitempty"`
- Pgfault uint64 `json:"pgfault,omitempty" yaml:"pgfault,omitempty"`
- InactiveFile uint64 `json:"inactive_file,omitempty" yaml:"inactive_file,omitempty"`
- TotalPgpgin uint64 `json:"total_pgpgin,omitempty" yaml:"total_pgpgin,omitempty"`
- HierarchicalMemswLimit uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty"`
- Swap uint64 `json:"swap,omitempty" yaml:"swap,omitempty"`
- } `json:"stats,omitempty" yaml:"stats,omitempty"`
- MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty"`
- Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty"`
- Failcnt uint64 `json:"failcnt,omitempty" yaml:"failcnt,omitempty"`
- Limit uint64 `json:"limit,omitempty" yaml:"limit,omitempty"`
- } `json:"memory_stats,omitempty" yaml:"memory_stats,omitempty"`
- BlkioStats struct {
- IOServiceBytesRecursive []BlkioStatsEntry `json:"io_service_bytes_recursive,omitempty" yaml:"io_service_bytes_recursive,omitempty"`
- IOServicedRecursive []BlkioStatsEntry `json:"io_serviced_recursive,omitempty" yaml:"io_serviced_recursive,omitempty"`
- IOQueueRecursive []BlkioStatsEntry `json:"io_queue_recursive,omitempty" yaml:"io_queue_recursive,omitempty"`
- IOServiceTimeRecursive []BlkioStatsEntry `json:"io_service_time_recursive,omitempty" yaml:"io_service_time_recursive,omitempty"`
- IOWaitTimeRecursive []BlkioStatsEntry `json:"io_wait_time_recursive,omitempty" yaml:"io_wait_time_recursive,omitempty"`
- IOMergedRecursive []BlkioStatsEntry `json:"io_merged_recursive,omitempty" yaml:"io_merged_recursive,omitempty"`
- IOTimeRecursive []BlkioStatsEntry `json:"io_time_recursive,omitempty" yaml:"io_time_recursive,omitempty"`
- SectorsRecursive []BlkioStatsEntry `json:"sectors_recursive,omitempty" yaml:"sectors_recursive,omitempty"`
- } `json:"blkio_stats,omitempty" yaml:"blkio_stats,omitempty"`
- CPUStats CPUStats `json:"cpu_stats,omitempty" yaml:"cpu_stats,omitempty"`
- PreCPUStats CPUStats `json:"precpu_stats,omitempty"`
- }
- // NetworkStats is a stats entry for network stats
- type NetworkStats struct {
- RxDropped uint64 `json:"rx_dropped,omitempty" yaml:"rx_dropped,omitempty"`
- RxBytes uint64 `json:"rx_bytes,omitempty" yaml:"rx_bytes,omitempty"`
- RxErrors uint64 `json:"rx_errors,omitempty" yaml:"rx_errors,omitempty"`
- TxPackets uint64 `json:"tx_packets,omitempty" yaml:"tx_packets,omitempty"`
- TxDropped uint64 `json:"tx_dropped,omitempty" yaml:"tx_dropped,omitempty"`
- RxPackets uint64 `json:"rx_packets,omitempty" yaml:"rx_packets,omitempty"`
- TxErrors uint64 `json:"tx_errors,omitempty" yaml:"tx_errors,omitempty"`
- TxBytes uint64 `json:"tx_bytes,omitempty" yaml:"tx_bytes,omitempty"`
- }
- // CPUStats is a stats entry for cpu stats
- type CPUStats struct {
- CPUUsage struct {
- PercpuUsage []uint64 `json:"percpu_usage,omitempty" yaml:"percpu_usage,omitempty"`
- UsageInUsermode uint64 `json:"usage_in_usermode,omitempty" yaml:"usage_in_usermode,omitempty"`
- TotalUsage uint64 `json:"total_usage,omitempty" yaml:"total_usage,omitempty"`
- UsageInKernelmode uint64 `json:"usage_in_kernelmode,omitempty" yaml:"usage_in_kernelmode,omitempty"`
- } `json:"cpu_usage,omitempty" yaml:"cpu_usage,omitempty"`
- SystemCPUUsage uint64 `json:"system_cpu_usage,omitempty" yaml:"system_cpu_usage,omitempty"`
- ThrottlingData struct {
- Periods uint64 `json:"periods,omitempty"`
- ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
- ThrottledTime uint64 `json:"throttled_time,omitempty"`
- } `json:"throttling_data,omitempty" yaml:"throttling_data,omitempty"`
- }
- // BlkioStatsEntry is a stats entry for blkio_stats
- type BlkioStatsEntry struct {
- Major uint64 `json:"major,omitempty" yaml:"major,omitempty"`
- Minor uint64 `json:"minor,omitempty" yaml:"minor,omitempty"`
- Op string `json:"op,omitempty" yaml:"op,omitempty"`
- Value uint64 `json:"value,omitempty" yaml:"value,omitempty"`
- }
- // StatsOptions specify parameters to the Stats function.
- //
- // See https://goo.gl/GNmLHb for more details.
- type StatsOptions struct {
- ID string
- Stats chan<- *Stats
- Stream bool
- // A flag that enables stopping the stats operation
- Done <-chan bool
- // Initial connection timeout
- Timeout time.Duration
- // Timeout with no data is received, it's reset every time new data
- // arrives
- InactivityTimeout time.Duration `qs:"-"`
- }
- // Stats sends container statistics for the given container to the given channel.
- //
- // This function is blocking, similar to a streaming call for logs, and should be run
- // on a separate goroutine from the caller. Note that this function will block until
- // the given container is removed, not just exited. When finished, this function
- // will close the given channel. Alternatively, function can be stopped by
- // signaling on the Done channel.
- //
- // See https://goo.gl/GNmLHb for more details.
- func (c *Client) Stats(opts StatsOptions) (retErr error) {
- errC := make(chan error, 1)
- readCloser, writeCloser := io.Pipe()
- defer func() {
- close(opts.Stats)
- select {
- case err := <-errC:
- if err != nil && retErr == nil {
- retErr = err
- }
- default:
- // No errors
- }
- if err := readCloser.Close(); err != nil && retErr == nil {
- retErr = err
- }
- }()
- go func() {
- err := c.stream("GET", fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{
- rawJSONStream: true,
- useJSONDecoder: true,
- stdout: writeCloser,
- timeout: opts.Timeout,
- inactivityTimeout: opts.InactivityTimeout,
- })
- if err != nil {
- dockerError, ok := err.(*Error)
- if ok {
- if dockerError.Status == http.StatusNotFound {
- err = &NoSuchContainer{ID: opts.ID}
- }
- }
- }
- if closeErr := writeCloser.Close(); closeErr != nil && err == nil {
- err = closeErr
- }
- errC <- err
- close(errC)
- }()
- quit := make(chan struct{})
- defer close(quit)
- go func() {
- // block here waiting for the signal to stop function
- select {
- case <-opts.Done:
- readCloser.Close()
- case <-quit:
- return
- }
- }()
- decoder := json.NewDecoder(readCloser)
- stats := new(Stats)
- for err := decoder.Decode(stats); err != io.EOF; err = decoder.Decode(stats) {
- if err != nil {
- return err
- }
- opts.Stats <- stats
- stats = new(Stats)
- }
- return nil
- }
- // KillContainerOptions represents the set of options that can be used in a
- // call to KillContainer.
- //
- // See https://goo.gl/hkS9i8 for more details.
- type KillContainerOptions struct {
- // The ID of the container.
- ID string `qs:"-"`
- // The signal to send to the container. When omitted, Docker server
- // will assume SIGKILL.
- Signal Signal
- }
- // KillContainer sends a signal to a container, returning an error in case of
- // failure.
- //
- // See https://goo.gl/hkS9i8 for more details.
- func (c *Client) KillContainer(opts KillContainerOptions) error {
- path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
- resp, err := c.do("POST", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: opts.ID}
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // RemoveContainerOptions encapsulates options to remove a container.
- //
- // See https://goo.gl/RQyX62 for more details.
- type RemoveContainerOptions struct {
- // The ID of the container.
- ID string `qs:"-"`
- // A flag that indicates whether Docker should remove the volumes
- // associated to the container.
- RemoveVolumes bool `qs:"v"`
- // A flag that indicates whether Docker should remove the container
- // even if it is currently running.
- Force bool
- }
- // RemoveContainer removes a container, returning an error in case of failure.
- //
- // See https://goo.gl/RQyX62 for more details.
- func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
- path := "/containers/" + opts.ID + "?" + queryString(opts)
- resp, err := c.do("DELETE", path, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: opts.ID}
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // UploadToContainerOptions is the set of options that can be used when
- // uploading an archive into a container.
- //
- // See https://goo.gl/Ss97HW for more details.
- type UploadToContainerOptions struct {
- InputStream io.Reader `json:"-" qs:"-"`
- Path string `qs:"path"`
- NoOverwriteDirNonDir bool `qs:"noOverwriteDirNonDir"`
- }
- // UploadToContainer uploads a tar archive to be extracted to a path in the
- // filesystem of the container.
- //
- // See https://goo.gl/Ss97HW for more details.
- func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) error {
- url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
- return c.stream("PUT", url, streamOptions{
- in: opts.InputStream,
- })
- }
- // DownloadFromContainerOptions is the set of options that can be used when
- // downloading resources from a container.
- //
- // See https://goo.gl/KnZJDX for more details.
- type DownloadFromContainerOptions struct {
- OutputStream io.Writer `json:"-" qs:"-"`
- Path string `qs:"path"`
- InactivityTimeout time.Duration `qs:"-"`
- }
- // DownloadFromContainer downloads a tar archive of files or folders in a container.
- //
- // See https://goo.gl/KnZJDX for more details.
- func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOptions) error {
- url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
- return c.stream("GET", url, streamOptions{
- setRawTerminal: true,
- stdout: opts.OutputStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- // CopyFromContainerOptions has been DEPRECATED, please use DownloadFromContainerOptions along with DownloadFromContainer.
- //
- // See https://goo.gl/R2jevW for more details.
- type CopyFromContainerOptions struct {
- OutputStream io.Writer `json:"-"`
- Container string `json:"-"`
- Resource string
- }
- // CopyFromContainer has been DEPRECATED, please use DownloadFromContainerOptions along with DownloadFromContainer.
- //
- // See https://goo.gl/R2jevW for more details.
- func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
- if opts.Container == "" {
- return &NoSuchContainer{ID: opts.Container}
- }
- url := fmt.Sprintf("/containers/%s/copy", opts.Container)
- resp, err := c.do("POST", url, doOptions{data: opts})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return &NoSuchContainer{ID: opts.Container}
- }
- return err
- }
- defer resp.Body.Close()
- _, err = io.Copy(opts.OutputStream, resp.Body)
- return err
- }
- // WaitContainer blocks until the given container stops, return the exit code
- // of the container status.
- //
- // See https://goo.gl/Gc1rge for more details.
- func (c *Client) WaitContainer(id string) (int, error) {
- resp, err := c.do("POST", "/containers/"+id+"/wait", doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return 0, &NoSuchContainer{ID: id}
- }
- return 0, err
- }
- defer resp.Body.Close()
- var r struct{ StatusCode int }
- if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
- return 0, err
- }
- return r.StatusCode, nil
- }
- // CommitContainerOptions aggregates parameters to the CommitContainer method.
- //
- // See https://goo.gl/mqfoCw for more details.
- type CommitContainerOptions struct {
- Container string
- Repository string `qs:"repo"`
- Tag string
- Message string `qs:"comment"`
- Author string
- Run *Config `qs:"-"`
- }
- // CommitContainer creates a new image from a container's changes.
- //
- // See https://goo.gl/mqfoCw for more details.
- func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
- path := "/commit?" + queryString(opts)
- resp, err := c.do("POST", path, doOptions{data: opts.Run})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return nil, &NoSuchContainer{ID: opts.Container}
- }
- return nil, err
- }
- defer resp.Body.Close()
- var image Image
- if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
- return nil, err
- }
- return &image, nil
- }
- // AttachToContainerOptions is the set of options that can be used when
- // attaching to a container.
- //
- // See https://goo.gl/NKpkFk for more details.
- type AttachToContainerOptions struct {
- Container string `qs:"-"`
- InputStream io.Reader `qs:"-"`
- OutputStream io.Writer `qs:"-"`
- ErrorStream io.Writer `qs:"-"`
- // Get container logs, sending it to OutputStream.
- Logs bool
- // Stream the response?
- Stream bool
- // Attach to stdin, and use InputStream.
- Stdin bool
- // Attach to stdout, and use OutputStream.
- Stdout bool
- // Attach to stderr, and use ErrorStream.
- Stderr bool
- // If set, after a successful connect, a sentinel will be sent and then the
- // client will block on receive before continuing.
- //
- // It must be an unbuffered channel. Using a buffered channel can lead
- // to unexpected behavior.
- Success chan struct{}
- // Use raw terminal? Usually true when the container contains a TTY.
- RawTerminal bool `qs:"-"`
- }
- // AttachToContainer attaches to a container, using the given options.
- //
- // See https://goo.gl/NKpkFk for more details.
- func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
- cw, err := c.AttachToContainerNonBlocking(opts)
- if err != nil {
- return err
- }
- return cw.Wait()
- }
- // AttachToContainerNonBlocking attaches to a container, using the given options.
- // This function does not block.
- //
- // See https://goo.gl/NKpkFk for more details.
- func (c *Client) AttachToContainerNonBlocking(opts AttachToContainerOptions) (CloseWaiter, error) {
- if opts.Container == "" {
- return nil, &NoSuchContainer{ID: opts.Container}
- }
- path := "/containers/" + opts.Container + "/attach?" + queryString(opts)
- return c.hijack("POST", path, hijackOptions{
- success: opts.Success,
- setRawTerminal: opts.RawTerminal,
- in: opts.InputStream,
- stdout: opts.OutputStream,
- stderr: opts.ErrorStream,
- })
- }
- // LogsOptions represents the set of options used when getting logs from a
- // container.
- //
- // See https://goo.gl/yl8PGm for more details.
- type LogsOptions struct {
- Container string `qs:"-"`
- OutputStream io.Writer `qs:"-"`
- ErrorStream io.Writer `qs:"-"`
- InactivityTimeout time.Duration `qs:"-"`
- Follow bool
- Stdout bool
- Stderr bool
- Since int64
- Timestamps bool
- Tail string
- // Use raw terminal? Usually true when the container contains a TTY.
- RawTerminal bool `qs:"-"`
- }
- // Logs gets stdout and stderr logs from the specified container.
- //
- // See https://goo.gl/yl8PGm for more details.
- func (c *Client) Logs(opts LogsOptions) error {
- if opts.Container == "" {
- return &NoSuchContainer{ID: opts.Container}
- }
- if opts.Tail == "" {
- opts.Tail = "all"
- }
- path := "/containers/" + opts.Container + "/logs?" + queryString(opts)
- return c.stream("GET", path, streamOptions{
- setRawTerminal: opts.RawTerminal,
- stdout: opts.OutputStream,
- stderr: opts.ErrorStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- // ResizeContainerTTY resizes the terminal to the given height and width.
- //
- // See https://goo.gl/xERhCc for more details.
- func (c *Client) ResizeContainerTTY(id string, height, width int) error {
- params := make(url.Values)
- params.Set("h", strconv.Itoa(height))
- params.Set("w", strconv.Itoa(width))
- resp, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), doOptions{})
- if err != nil {
- return err
- }
- resp.Body.Close()
- return nil
- }
- // ExportContainerOptions is the set of parameters to the ExportContainer
- // method.
- //
- // See https://goo.gl/dOkTyk for more details.
- type ExportContainerOptions struct {
- ID string
- OutputStream io.Writer
- InactivityTimeout time.Duration `qs:"-"`
- }
- // ExportContainer export the contents of container id as tar archive
- // and prints the exported contents to stdout.
- //
- // See https://goo.gl/dOkTyk for more details.
- func (c *Client) ExportContainer(opts ExportContainerOptions) error {
- if opts.ID == "" {
- return &NoSuchContainer{ID: opts.ID}
- }
- url := fmt.Sprintf("/containers/%s/export", opts.ID)
- return c.stream("GET", url, streamOptions{
- setRawTerminal: true,
- stdout: opts.OutputStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- // NoSuchContainer is the error returned when a given container does not exist.
- type NoSuchContainer struct {
- ID string
- Err error
- }
- func (err *NoSuchContainer) Error() string {
- if err.Err != nil {
- return err.Err.Error()
- }
- return "No such container: " + err.ID
- }
- // ContainerAlreadyRunning is the error returned when a given container is
- // already running.
- type ContainerAlreadyRunning struct {
- ID string
- }
- func (err *ContainerAlreadyRunning) Error() string {
- return "Container already running: " + err.ID
- }
- // ContainerNotRunning is the error returned when a given container is not
- // running.
- type ContainerNotRunning struct {
- ID string
- }
- func (err *ContainerNotRunning) Error() string {
- return "Container not running: " + err.ID
- }
|