Add support for shm_size to docker driver. Required update of go-dockerclient

to more recent version (ba4298ecf4cfa216f1cce2befbf653359f62c722).
This commit is contained in:
Daniel Kerwin
2016-05-27 12:30:04 +02:00
parent 7455413551
commit 98b227fecf
17 changed files with 402 additions and 183 deletions

View File

@@ -1,2 +0,0 @@
# temporary symlink for testing
testing/data/symlink

View File

@@ -1,28 +0,0 @@
language: go
sudo: required
go:
- 1.3.3
- 1.4.2
- 1.5.3
- 1.6
- tip
env:
- GOARCH=amd64 DOCKER_VERSION=1.7.1
- GOARCH=386 DOCKER_VERSION=1.7.1
- GOARCH=amd64 DOCKER_VERSION=1.8.3
- GOARCH=386 DOCKER_VERSION=1.8.3
- GOARCH=amd64 DOCKER_VERSION=1.9.1
- GOARCH=386 DOCKER_VERSION=1.9.1
- GOARCH=amd64 DOCKER_VERSION=1.10.3
- GOARCH=386 DOCKER_VERSION=1.10.3
install:
- travis_retry make prepare_docker
script:
- travis-scripts/run-tests.bash
- DOCKER_HOST=tcp://127.0.0.1:2375 make integration
services:
- docker
matrix:
fast_finish: true
allow_failures:
- go: tip

View File

@@ -49,6 +49,7 @@ Fabio Rehm <fgrehm@gmail.com>
Fatih Arslan <ftharsln@gmail.com>
Flavia Missi <flaviamissi@gmail.com>
Francisco Souza <f@souza.cc>
Frank Groeneveld <frank@frankgroeneveld.nl>
George Moura <gwmoura@gmail.com>
Grégoire Delattre <gregoire.delattre@gmail.com>
Guillermo Álvarez Fernández <guillermo@cientifico.net>
@@ -100,12 +101,16 @@ Peter Jihoon Kim <raingrove@gmail.com>
Phil Lu <lu@stackengine.com>
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
Rafe Colton <rafael.colton@gmail.com>
Raphaël Pinson <raphael.pinson@camptocamp.com>
Rob Miller <rob@kalistra.com>
Robert Williamson <williamson.robert@gmail.com>
Roman Khlystik <roman.khlystik@gmail.com>
Salvador Gironès <salvadorgirones@gmail.com>
Sam Rijs <srijs@airpost.net>
Sami Wagiaalla <swagiaal@redhat.com>
Samuel Archambault <sarchambault@lapresse.ca>
Samuel Karp <skarp@amazon.com>
Seth Jennings <sjenning@redhat.com>
Silas Sewell <silas@sewell.org>
Simon Eskildsen <sirup@sirupsen.com>
Simon Menke <simon.menke@gmail.com>
@@ -123,6 +128,7 @@ Tonic <tonicbupt@gmail.com>
ttyh061 <ttyh061@gmail.com>
Victor Marmol <vmarmol@google.com>
Vincenzo Prignano <vincenzo.prignano@gmail.com>
Vlad Alexandru Ionescu <vlad.alexandru.ionescu@gmail.com>
Wiliam Souza <wiliamsouza83@gmail.com>
Ye Yin <eyniy@qq.com>
Yu, Zou <zouyu7@huawei.com>

View File

@@ -28,7 +28,6 @@ lint:
exit $${status:-0}
vet:
@-go get -v golang.org/x/tools/cmd/vet
$(foreach pkg,$(PKGS),go vet $(pkg);)
fmt:
@@ -39,15 +38,6 @@ fmtcheck:
[ -n "$${output}" ] && echo "$${output}" && export status=1; \
exit $${status:-0}
prepare_docker:
sudo stop docker || true
sudo rm -rf /var/lib/docker
sudo rm -f `which docker`
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt-get update
sudo apt-get install docker-engine=$(DOCKER_VERSION)-0~$(shell lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
pretest: lint vet fmtcheck
gotest:

View File

@@ -1,10 +1,11 @@
# go-dockerclient
[![Travis](https://img.shields.io/travis/fsouza/go-dockerclient.svg?style=flat-square)](https://travis-ci.org/fsouza/go-dockerclient)
[![Travis](https://img.shields.io/travis/fsouza/go-dockerclient/master.svg?style=flat-square)](https://travis-ci.org/fsouza/go-dockerclient)
[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://godoc.org/github.com/fsouza/go-dockerclient)
This package presents a client for the Docker remote API. It also provides
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/swarm-api/).
It currently supports the Docker API up to version 1.23.
This package also provides support for docker's network API, which is a simple
passthrough to the libnetwork remote API. Note that docker's network API is

View File

@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
@@ -82,10 +83,12 @@ func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) {
buf.ReadFrom(r)
byteData := buf.Bytes()
var confsWrapper map[string]map[string]dockerConfig
confsWrapper := struct {
Auths map[string]dockerConfig `json:"auths"`
}{}
if err := json.Unmarshal(byteData, &confsWrapper); err == nil {
if confs, ok := confsWrapper["auths"]; ok {
return confs, nil
if len(confsWrapper.Auths) > 0 {
return confsWrapper.Auths, nil
}
}
@@ -120,17 +123,36 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
return c, nil
}
// AuthStatus returns the authentication status for Docker API versions >= 1.23.
type AuthStatus struct {
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
IdentityToken string `json:"IdentityToken,omitempty" yaml:"IdentityToken,omitempty"`
}
// AuthCheck validates the given credentials. It returns nil if successful.
//
// See https://goo.gl/m2SleN for more details.
func (c *Client) AuthCheck(conf *AuthConfiguration) error {
// For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty.`
//
// See https://goo.gl/6nsZkH for more details.
func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) {
var authStatus AuthStatus
if conf == nil {
return fmt.Errorf("conf is nil")
return authStatus, fmt.Errorf("conf is nil")
}
resp, err := c.do("POST", "/auth", doOptions{data: conf})
if err != nil {
return err
return authStatus, err
}
resp.Body.Close()
return nil
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return authStatus, err
}
if len(data) == 0 {
return authStatus, nil
}
if err := json.Unmarshal(data, &authStatus); err != nil {
return authStatus, err
}
return authStatus, nil
}

17
vendor/github.com/fsouza/go-dockerclient/cancelable.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2016 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.
// +build go1.5
package docker
import "net/http"
func cancelable(client *http.Client, req *http.Request) func() {
ch := make(chan struct{})
req.Cancel = ch
return func() {
close(ch)
}
}

View File

@@ -0,0 +1,19 @@
// Copyright 2016 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.
// +build !go1.5
package docker
import "net/http"
func cancelable(client *http.Client, req *http.Request) func() {
return func() {
if rc, ok := client.Transport.(interface {
CancelRequest(*http.Request)
}); ok {
rc.CancelRequest(req)
}
}
}

View File

@@ -27,6 +27,7 @@ import (
"runtime"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts"
@@ -44,6 +45,9 @@ var (
// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
// ErrInactivityTimeout is returned when a streamable call has been inactive for some time.
ErrInactivityTimeout = errors.New("inactivity time exceeded timeout")
apiVersion112, _ = NewAPIVersion("1.12")
apiVersion119, _ = NewAPIVersion("1.19")
@@ -436,8 +440,11 @@ type streamOptions struct {
in io.Reader
stdout io.Writer
stderr io.Writer
// timeout is the inital connection timeout
// timeout is the initial connection timeout
timeout time.Duration
// Timeout with no data is received, it's reset every time new data
// arrives
inactivityTimeout time.Duration
}
func (c *Client) stream(method, path string, streamOptions streamOptions) error {
@@ -470,11 +477,13 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
if streamOptions.stderr == nil {
streamOptions.stderr = ioutil.Discard
}
cancelRequest := cancelable(c.HTTPClient, req)
if protocol == "unix" {
dial, err := c.Dialer.Dial(protocol, address)
if err != nil {
return err
}
cancelRequest = func() { dial.Close() }
defer dial.Close()
breader := bufio.NewReader(dial)
err = req.Write(dial)
@@ -509,33 +518,24 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
return newError(resp)
}
if streamOptions.useJSONDecoder || resp.Header.Get("Content-Type") == "application/json" {
// if we want to get raw json stream, just copy it back to output
// without decoding it
if streamOptions.rawJSONStream {
_, err = io.Copy(streamOptions.stdout, resp.Body)
return err
var canceled uint32
if streamOptions.inactivityTimeout > 0 {
ch := handleInactivityTimeout(&streamOptions, cancelRequest, &canceled)
defer close(ch)
}
err = handleStreamResponse(resp, &streamOptions)
if err != nil {
if atomic.LoadUint32(&canceled) != 0 {
return ErrInactivityTimeout
}
dec := json.NewDecoder(resp.Body)
for {
var m jsonMessage
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
return err
}
if m.Stream != "" {
fmt.Fprint(streamOptions.stdout, m.Stream)
} else if m.Progress != "" {
fmt.Fprintf(streamOptions.stdout, "%s %s\r", m.Status, m.Progress)
} else if m.Error != "" {
return errors.New(m.Error)
}
if m.Status != "" {
fmt.Fprintln(streamOptions.stdout, m.Status)
}
}
} else {
return err
}
return nil
}
func handleStreamResponse(resp *http.Response, streamOptions *streamOptions) error {
var err error
if !streamOptions.useJSONDecoder && resp.Header.Get("Content-Type") != "application/json" {
if streamOptions.setRawTerminal {
_, err = io.Copy(streamOptions.stdout, resp.Body)
} else {
@@ -543,9 +543,74 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
}
return err
}
// if we want to get raw json stream, just copy it back to output
// without decoding it
if streamOptions.rawJSONStream {
_, err = io.Copy(streamOptions.stdout, resp.Body)
return err
}
dec := json.NewDecoder(resp.Body)
for {
var m jsonMessage
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
return err
}
if m.Stream != "" {
fmt.Fprint(streamOptions.stdout, m.Stream)
} else if m.Progress != "" {
fmt.Fprintf(streamOptions.stdout, "%s %s\r", m.Status, m.Progress)
} else if m.Error != "" {
return errors.New(m.Error)
}
if m.Status != "" {
fmt.Fprintln(streamOptions.stdout, m.Status)
}
}
return nil
}
type proxyWriter struct {
io.Writer
calls uint64
}
func (p *proxyWriter) callCount() uint64 {
return atomic.LoadUint64(&p.calls)
}
func (p *proxyWriter) Write(data []byte) (int, error) {
atomic.AddUint64(&p.calls, 1)
return p.Writer.Write(data)
}
func handleInactivityTimeout(options *streamOptions, cancelRequest func(), canceled *uint32) chan<- struct{} {
done := make(chan struct{})
proxyStdout := &proxyWriter{Writer: options.stdout}
proxyStderr := &proxyWriter{Writer: options.stderr}
options.stdout = proxyStdout
options.stderr = proxyStderr
go func() {
var lastCallCount uint64
for {
select {
case <-time.After(options.inactivityTimeout):
case <-done:
return
}
curCallCount := proxyStdout.callCount() + proxyStderr.callCount()
if curCallCount == lastCallCount {
atomic.AddUint32(canceled, 1)
cancelRequest()
return
}
lastCallCount = curCallCount
}
}()
return done
}
type hijackOptions struct {
success chan struct{}
setRawTerminal bool

View File

@@ -14,6 +14,8 @@ import (
"strconv"
"strings"
"time"
"github.com/fsouza/go-dockerclient/external/github.com/docker/go-units"
)
// ErrContainerAlreadyExists is the error returned by CreateContainer when the
@@ -40,6 +42,17 @@ type APIPort struct {
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 {
@@ -47,6 +60,7 @@ type APIContainers struct {
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"`
@@ -54,6 +68,7 @@ type APIContainers struct {
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
@@ -99,26 +114,73 @@ func (p Port) Proto() string {
// State represents the state of a container.
type State struct {
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"`
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"`
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 the string representation of a state.
// 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"
}
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt))
if s.Restarting {
return "restarting"
}
return "running"
}
return fmt.Sprintf("Exit %d", s.ExitCode)
if s.Dead {
return "dead"
}
if s.StartedAt.IsZero() {
return "created"
}
return "exited"
}
// PortBinding represents the host/container port mapping as returned in the
@@ -213,6 +275,7 @@ type Config struct {
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"`
@@ -279,6 +342,12 @@ type SwarmNode struct {
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 {
@@ -306,10 +375,11 @@ type Container struct {
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"`
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"`
@@ -320,16 +390,17 @@ type Container struct {
//
// 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"`
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
@@ -541,6 +612,7 @@ type HostConfig struct {
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"`
@@ -570,6 +642,7 @@ type HostConfig struct {
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.
@@ -697,7 +770,10 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
//
// See https://goo.gl/GNmLHb for more details.
type Stats struct {
Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
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 {
@@ -800,6 +876,9 @@ type StatsOptions struct {
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.
@@ -834,10 +913,11 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
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,
rawJSONStream: true,
useJSONDecoder: true,
stdout: writeCloser,
timeout: opts.Timeout,
inactivityTimeout: opts.InactivityTimeout,
})
if err != nil {
dockerError, ok := err.(*Error)
@@ -967,8 +1047,9 @@ func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) err
//
// See https://goo.gl/KnZJDX for more details.
type DownloadFromContainerOptions struct {
OutputStream io.Writer `json:"-" qs:"-"`
Path string `qs:"path"`
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.
@@ -978,8 +1059,9 @@ func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOpti
url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
return c.stream("GET", url, streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
@@ -1134,15 +1216,16 @@ func (c *Client) AttachToContainerNonBlocking(opts AttachToContainerOptions) (Cl
//
// See https://goo.gl/yl8PGm for more details.
type LogsOptions struct {
Container string `qs:"-"`
OutputStream io.Writer `qs:"-"`
ErrorStream io.Writer `qs:"-"`
Follow bool
Stdout bool
Stderr bool
Since int64
Timestamps bool
Tail string
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:"-"`
@@ -1160,9 +1243,10 @@ func (c *Client) Logs(opts LogsOptions) error {
}
path := "/containers/" + opts.Container + "/logs?" + queryString(opts)
return c.stream("GET", path, streamOptions{
setRawTerminal: opts.RawTerminal,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,
setRawTerminal: opts.RawTerminal,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
@@ -1186,8 +1270,9 @@ func (c *Client) ResizeContainerTTY(id string, height, width int) error {
//
// See https://goo.gl/dOkTyk for more details.
type ExportContainerOptions struct {
ID string
OutputStream io.Writer
ID string
OutputStream io.Writer
InactivityTimeout time.Duration `qs:"-"`
}
// ExportContainer export the contents of container id as tar archive
@@ -1200,8 +1285,9 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error {
}
url := fmt.Sprintf("/containers/%s/export", opts.ID)
return c.stream("GET", url, streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}

View File

@@ -334,8 +334,8 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan
// transformEvent takes an event and determines what version it is from
// then populates both versions of the event
func transformEvent(event *APIEvents) {
// if <= 1.21, `status` and `ID` will be populated
if event.Status != "" && event.ID != "" {
// if event version is <= 1.21 there will be no Action and no Type
if event.Action == "" && event.Type == "" {
event.Action = event.Status
event.Actor.ID = event.ID
event.Actor.Attributes = map[string]string{}
@@ -349,16 +349,22 @@ func transformEvent(event *APIEvents) {
}
}
} else {
if event.Type == "image" || event.Type == "container" {
event.Status = event.Action
} else {
// Because just the Status has been overloaded with different Types
// if an event is not for an image or a container, we prepend the type
// to avoid problems for people relying on actions being only for
// images and containers
event.Status = event.Type + ":" + event.Action
if event.Status == "" {
if event.Type == "image" || event.Type == "container" {
event.Status = event.Action
} else {
// Because just the Status has been overloaded with different Types
// if an event is not for an image or a container, we prepend the type
// to avoid problems for people relying on actions being only for
// images and containers
event.Status = event.Type + ":" + event.Action
}
}
if event.ID == "" {
event.ID = event.Actor.ID
}
if event.From == "" {
event.From = event.Actor.Attributes["image"]
}
event.ID = event.Actor.ID
event.From = event.Actor.Attributes["image"]
}
}

View File

@@ -29,9 +29,16 @@ type APIImages struct {
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
}
// RootFS represents the underlying layers used by an image
type RootFS struct {
Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty"`
}
// Image is the type representing a docker image and its various properties
type Image struct {
ID string `json:"Id" yaml:"Id"`
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"`
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"`
Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"`
@@ -44,6 +51,7 @@ type Image struct {
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"`
RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty"`
RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty"`
}
// ImagePre012 serves the same purpose as the Image type except that it is for
@@ -235,8 +243,9 @@ type PushImageOptions struct {
// Registry server to push the image
Registry string
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
}
// PushImage pushes an image to a remote registry, logging progress to w.
@@ -257,10 +266,11 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error
opts.Name = ""
path := "/images/" + name + "/push?" + queryString(&opts)
return c.stream("POST", path, streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
stdout: opts.OutputStream,
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
@@ -269,11 +279,13 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error
//
// See https://goo.gl/iJkZjD for more details.
type PullImageOptions struct {
Repository string `qs:"fromImage"`
Registry string
Tag string
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
Repository string `qs:"fromImage"`
Registry string
Tag string
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
}
// PullImage pulls an image from a remote registry, logging progress to
@@ -289,17 +301,18 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error
if err != nil {
return err
}
return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream)
return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout)
}
func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool) error {
func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration) error {
path := "/images/create?" + qs
return c.stream("POST", path, streamOptions{
setRawTerminal: true,
rawJSONStream: rawJSONStream,
headers: headers,
in: in,
stdout: w,
setRawTerminal: true,
headers: headers,
in: in,
stdout: w,
rawJSONStream: rawJSONStream,
inactivityTimeout: timeout,
})
}
@@ -324,8 +337,9 @@ func (c *Client) LoadImage(opts LoadImageOptions) error {
//
// See https://goo.gl/le7vK8 for more details.
type ExportImageOptions struct {
Name string
OutputStream io.Writer
Name string
OutputStream io.Writer
InactivityTimeout time.Duration `qs:"-"`
}
// ExportImage exports an image (as a tar file) into the stream.
@@ -333,8 +347,9 @@ type ExportImageOptions struct {
// See https://goo.gl/le7vK8 for more details.
func (c *Client) ExportImage(opts ExportImageOptions) error {
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
@@ -342,8 +357,9 @@ func (c *Client) ExportImage(opts ExportImageOptions) error {
//
// See https://goo.gl/huC7HA for more details.
type ExportImagesOptions struct {
Names []string
OutputStream io.Writer `qs:"-"`
Names []string
OutputStream io.Writer `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
}
// ExportImages exports one or more images (as a tar file) into the stream
@@ -354,8 +370,9 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
return ErrMustSpecifyNames
}
return c.stream("GET", "/images/get?"+queryString(&opts), streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
@@ -368,9 +385,10 @@ type ImportImageOptions struct {
Source string `qs:"fromSrc"`
Tag string `qs:"tag"`
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
}
// ImportImage imports an image from a url, a file or stdin
@@ -391,7 +409,7 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
opts.InputStream = f
opts.Source = "-"
}
return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream)
return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout)
}
// BuildImageOptions present the set of informations available for building an
@@ -422,6 +440,7 @@ type BuildImageOptions struct {
ContextDir string `qs:"-"`
Ulimits []ULimit `qs:"-"`
BuildArgs []BuildArg `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
}
// BuildArg represents arguments that can be passed to the image when building
@@ -487,11 +506,12 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
}
return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
in: opts.InputStream,
stdout: opts.OutputStream,
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
in: opts.InputStream,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
@@ -584,10 +604,10 @@ type APIImageSearch struct {
// See https://goo.gl/AYjyrF for more details.
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
resp, err := c.do("GET", "/images/search?term="+term, doOptions{})
defer resp.Body.Close()
if err != nil {
return nil, err
}
defer resp.Body.Close()
var searchResult []APIImageSearch
if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
return nil, err

View File

@@ -28,6 +28,7 @@ type Network struct {
Containers map[string]Endpoint
Options map[string]string
Internal bool
EnableIPv6 bool `json:"EnableIPv6"`
}
// Endpoint contains network resources allocated and used for a container in a network
@@ -111,7 +112,9 @@ type CreateNetworkOptions struct {
CheckDuplicate bool `json:"CheckDuplicate"`
Driver string `json:"Driver"`
IPAM IPAMOptions `json:"IPAM"`
Options map[string]interface{} `json:"options"`
Options map[string]interface{} `json:"Options"`
Internal bool `json:"Internal"`
EnableIPv6 bool `json:"EnableIPv6"`
}
// IPAMOptions controls IP Address Management when creating a network
@@ -119,7 +122,7 @@ type CreateNetworkOptions struct {
// See https://goo.gl/T8kRVH for more details.
type IPAMOptions struct {
Driver string `json:"Driver"`
Config []IPAMConfig `json:"IPAMConfig"`
Config []IPAMConfig `json:"Config"`
}
// IPAMConfig represents IPAM configurations

View File

@@ -22,9 +22,10 @@ var (
//
// See https://goo.gl/FZA4BK for more details.
type Volume struct {
Name string `json:"Name" yaml:"Name"`
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"`
Mountpoint string `json:"Mountpoint,omitempty" yaml:"Mountpoint,omitempty"`
Name string `json:"Name" yaml:"Name"`
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"`
Mountpoint string `json:"Mountpoint,omitempty" yaml:"Mountpoint,omitempty"`
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
}
// ListVolumesOptions specify parameters to the ListVolumes function.