diff --git a/client/driver/docker.go b/client/driver/docker.go index 5372abbb525..9bc59c7a6e2 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -135,6 +135,38 @@ type DockerLoggingOpts struct { Config map[string]string `mapstructure:"-"` } +type Mount struct { + Target string `mapstructure:"target"` + Source string `mapstructure:"source"` + Type string `mapstructure:"type"` + ReadOnly bool `mapstructure:"readonly"` + BindOptions *BindOptions `mapstructure:"bind_options"` + VolumeOptions *VolumeOptions `mapstructure:"volume_options"` + TempfsOptions *TempfsOptions `mapstructure:"tempfs_options"` +} + +type BindOptions struct { + Propagation string `mapstructure:"propagation"` +} + +type VolumeOptions struct { + NoCopy bool `mapstructure:"no_copy"` + Labels map[string]string `mapstructure:"labels"` + DriverConfig VolumeDriverConfig `mapstructure:"driver_config"` +} + +// TempfsOptions contains optional configuration for the tempfs type +type TempfsOptions struct { + SizeBytes int64 `mapstructure:"size_bytes"` + Mode int `mapstructure:"mode"` +} + +// VolumeDriverConfig holds a map of volume driver specific options +type VolumeDriverConfig struct { + Name string `mapstructure:"name"` + Options map[string]string `mapstructure:"options"` +} + type DockerDriverConfig struct { ImageName string `mapstructure:"image"` // Container's Image Name LoadImage string `mapstructure:"load"` // LoadImage is a path to an image archive file @@ -165,6 +197,7 @@ type DockerDriverConfig struct { WorkDir string `mapstructure:"work_dir"` // Working directory inside the container Logging []DockerLoggingOpts `mapstructure:"logging"` // Logging options for syslog server Volumes []string `mapstructure:"volumes"` // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container + Mounts []Mount `mapstructure:"mounts"` // Docker volumes to mount VolumeDriver string `mapstructure:"volume_driver"` // Docker volume driver used for the container's volumes ForcePull bool `mapstructure:"force_pull"` // Always force pull before running image, useful if your tags are mutable MacAddress string `mapstructure:"mac_address"` // Pin mac address to container @@ -234,6 +267,40 @@ func NewDockerDriverConfig(task *structs.Task, env *env.TaskEnv) (*DockerDriverC } } + for i, m := range dconf.Mounts { + dconf.Mounts[i].Target = env.ReplaceEnv(m.Target) + dconf.Mounts[i].Source = env.ReplaceEnv(m.Source) + dconf.Mounts[i].Type = env.ReplaceEnv(m.Type) + if dconf.Mounts[i].Type == "" { + dconf.Mounts[i].Type = "volume" + } + if dconf.Mounts[i].Type != "volume" { + return nil, fmt.Errorf("mount type %v is not supported") + } + if m.BindOptions != nil { + dconf.Mounts[i].BindOptions.Propagation = env.ReplaceEnv(m.BindOptions.Propagation) + } + if m.VolumeOptions != nil { + if m.VolumeOptions.Labels != nil { + for k, v := range m.VolumeOptions.Labels { + if k != env.ReplaceEnv(k) { + delete(dconf.Mounts[i].VolumeOptions.Labels, k) + } + dconf.Mounts[i].VolumeOptions.Labels[env.ReplaceEnv(k)] = env.ReplaceEnv(v) + } + } + dconf.Mounts[i].VolumeOptions.DriverConfig.Name = env.ReplaceEnv(m.VolumeOptions.DriverConfig.Name) + if m.VolumeOptions.DriverConfig.Options != nil { + for k, v := range m.VolumeOptions.DriverConfig.Options { + if k != env.ReplaceEnv(k) { + delete(dconf.Mounts[i].VolumeOptions.DriverConfig.Options, k) + } + dconf.Mounts[i].VolumeOptions.DriverConfig.Options[env.ReplaceEnv(k)] = env.ReplaceEnv(v) + } + } + } + } + if len(dconf.Logging) > 0 { dconf.Logging[0].Config = mapMergeStrStr(dconf.Logging[0].ConfigRaw...) } @@ -452,6 +519,9 @@ func (d *DockerDriver) Validate(config map[string]interface{}) error { "volume_driver": &fields.FieldSchema{ Type: fields.TypeString, }, + "mounts": { + Type: fields.TypeArray, + }, "force_pull": &fields.FieldSchema{ Type: fields.TypeBool, }, @@ -935,6 +1005,38 @@ func (d *DockerDriver) createContainerConfig(ctx *ExecContext, task *structs.Tas } } + // Setup mounts + for _, m := range driverConfig.Mounts { + hm := docker.HostMount{ + Target: m.Target, + Source: m.Source, + Type: m.Type, + ReadOnly: m.ReadOnly, + } + if m.BindOptions != nil { + hm.BindOptions = &docker.BindOptions{ + Propagation: m.BindOptions.Propagation, + } + } + if m.VolumeOptions != nil { + hm.VolumeOptions = &docker.VolumeOptions{ + NoCopy: m.VolumeOptions.NoCopy, + Labels: m.VolumeOptions.Labels, + DriverConfig: docker.VolumeDriverConfig{ + Name: m.VolumeOptions.DriverConfig.Name, + Options: m.VolumeOptions.DriverConfig.Options, + }, + } + } + if m.TempfsOptions != nil { + hm.TempfsOptions = &docker.TempfsOptions{ + SizeBytes: m.TempfsOptions.SizeBytes, + Mode: m.TempfsOptions.Mode, + } + } + hostConfig.Mounts = append(hostConfig.Mounts, hm) + } + // set DNS search domains and extra hosts hostConfig.DNSSearch = driverConfig.DNSSearchDomains hostConfig.ExtraHosts = driverConfig.ExtraHosts diff --git a/vendor/github.com/fsouza/go-dockerclient/AUTHORS b/vendor/github.com/fsouza/go-dockerclient/AUTHORS index 36f3f865382..55dd8e3b371 100644 --- a/vendor/github.com/fsouza/go-dockerclient/AUTHORS +++ b/vendor/github.com/fsouza/go-dockerclient/AUTHORS @@ -8,6 +8,7 @@ Alex Dadgar Alfonso Acosta André Carvalho Andreas Jaekle +Andrew Snodgrass Andrews Medina Andrey Sibiryov Andy Goldstein @@ -35,25 +36,33 @@ Changping Chen Cheah Chu Yeow cheneydeng Chris Bednarski +Chris Stavropoulos Christian Stewart +Christophe Mourette +Clint Armstrong CMGS Colin Hebert Craig Jellick +Damon Wang Dan Williams Daniel, Dao Quang Minh Daniel Garcia Daniel Hiltgen +Daniel Tsui Darren Shepherd Dave Choi David Huie Dawn Chen +Derek Petersen Dinesh Subhraveti Drew Wells Ed Elias G. Schneevoigt Erez Horev Eric Anderson +Eric J. Holmes Eric Mountain +Erwin van Eyk Ethan Mosbaugh Ewout Prangsma Fabio Rehm @@ -72,6 +81,8 @@ He Simei Ivan Mikushin James Bardin James Nugent +Jamie Snell +Januar Wayong Jari Kolehmainen Jason Wilder Jawher Moussa @@ -82,8 +93,10 @@ Jen Andre Jérôme Laurens Jim Minter Johan Euphrosine +Johannes Scheuermann John Hughes Jorge Marey +Julian Einwag Kamil Domanski Karan Misra Ken Herner diff --git a/vendor/github.com/fsouza/go-dockerclient/README.markdown b/vendor/github.com/fsouza/go-dockerclient/README.markdown index 0a460ed4760..a9ffc17a0f6 100644 --- a/vendor/github.com/fsouza/go-dockerclient/README.markdown +++ b/vendor/github.com/fsouza/go-dockerclient/README.markdown @@ -103,10 +103,23 @@ All development commands can be seen in the [Makefile](Makefile). Commited code must pass: * [golint](https://github.com/golang/lint) (with some exceptions, see the Makefile). -* [go vet](https://godoc.org/golang.org/x/tools/cmd/vet) +* [go vet](https://golang.org/cmd/vet/) * [gofmt](https://golang.org/cmd/gofmt) * [go test](https://golang.org/cmd/go/#hdr-Test_packages) Running `make test` will check all of these. If your editor does not automatically call ``gofmt -s``, `make fmt` will format all go files in this repository. + +## Using with Docker 1.9 and Go 1.4 + +There's a tag for using go-dockerclient with Docker 1.9 (which requires +compiling go-dockerclient with Go 1.4), the tag name is ``docker-1.9/go-1.4``. + +The instructions below can be used to get a version of go-dockerclient that compiles with Go 1.4: + +``` +% git clone -b docker-1.9/go-1.4 https://github.com/fsouza/go-dockerclient.git $GOPATH/src/github.com/fsouza/go-dockerclient +% git clone -b v1.9.1 https://github.com/docker/docker.git $GOPATH/src/github.com/docker/docker +% go get github.com/fsouza/go-dockerclient +``` diff --git a/vendor/github.com/fsouza/go-dockerclient/appveyor.yml b/vendor/github.com/fsouza/go-dockerclient/appveyor.yml index e98da1f8768..be1729a5bbe 100644 --- a/vendor/github.com/fsouza/go-dockerclient/appveyor.yml +++ b/vendor/github.com/fsouza/go-dockerclient/appveyor.yml @@ -4,13 +4,18 @@ clone_depth: 2 clone_folder: c:\gopath\src\github.com\fsouza\go-dockerclient environment: GOPATH: c:\gopath - GOVERSION: 1.7.5 + matrix: + - GOVERSION: 1.7.5 + - GOVERSION: 1.8.3 + - GOVERSION: 1.9rc1 install: - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% - rmdir c:\go /s /q - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.zip - 7z x go%GOVERSION%.windows-amd64.zip -y -oC:\ > NUL build_script: - - go get -d -t ./... + - go get -race -d -t ./... test_script: - - go test ./... + - go test -race ./... +matrix: + fast_finish: true diff --git a/vendor/github.com/fsouza/go-dockerclient/client.go b/vendor/github.com/fsouza/go-dockerclient/client.go index 2f5ed88306c..625d4cd5a7d 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client.go +++ b/vendor/github.com/fsouza/go-dockerclient/client.go @@ -24,6 +24,7 @@ import ( "os" "path/filepath" "reflect" + "runtime" "strconv" "strings" "sync/atomic" @@ -33,7 +34,6 @@ import ( "github.com/docker/docker/pkg/homedir" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/stdcopy" - "github.com/hashicorp/go-cleanhttp" "golang.org/x/net/context" "golang.org/x/net/context/ctxhttp" ) @@ -58,6 +58,7 @@ var ( apiVersion112, _ = NewAPIVersion("1.12") apiVersion119, _ = NewAPIVersion("1.19") apiVersion124, _ = NewAPIVersion("1.24") + apiVersion125, _ = NewAPIVersion("1.25") ) // APIVersion is an internal representation of a version of the Remote API. @@ -211,7 +212,7 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro } } c := &Client{ - HTTPClient: cleanhttp.DefaultClient(), + HTTPClient: defaultClient(), Dialer: &net.Dialer{}, endpoint: endpoint, endpointURL: u, @@ -325,7 +326,7 @@ func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, } tlsConfig.RootCAs = caPool } - tr := cleanhttp.DefaultTransport() + tr := defaultTransport() tr.TLSClientConfig = tlsConfig if err != nil { return nil, err @@ -497,6 +498,7 @@ type streamOptions struct { in io.Reader stdout io.Writer stderr io.Writer + reqSent chan struct{} // timeout is the initial connection timeout timeout time.Duration // Timeout with no data is received, it's reset every time new data @@ -575,6 +577,9 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error dial.SetDeadline(time.Now().Add(streamOptions.timeout)) } + if streamOptions.reqSent != nil { + close(streamOptions.reqSent) + } if resp, err = http.ReadResponse(breader, req); err != nil { // Cancel timeout for future I/O operations if streamOptions.timeout > 0 { @@ -593,6 +598,9 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error } return chooseError(subCtx, err) } + if streamOptions.reqSent != nil { + close(streamOptions.reqSent) + } } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 400 { @@ -1032,3 +1040,41 @@ func getDockerEnv() (*dockerEnv, error) { dockerCertPath: dockerCertPath, }, nil } + +// defaultTransport returns a new http.Transport with similar default values to +// http.DefaultTransport, but with idle connections and keepalives disabled. +func defaultTransport() *http.Transport { + transport := defaultPooledTransport() + transport.DisableKeepAlives = true + transport.MaxIdleConnsPerHost = -1 + return transport +} + +// defaultPooledTransport returns a new http.Transport with similar default +// values to http.DefaultTransport. Do not use this for transient transports as +// it can leak file descriptors over time. Only use this for transports that +// will be re-used for the same host(s). +func defaultPooledTransport() *http.Transport { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, + } + return transport +} + +// defaultClient returns a new http.Client with similar default values to +// http.Client, but with a non-shared Transport, idle connections disabled, and +// keepalives disabled. +func defaultClient() *http.Client { + return &http.Client{ + Transport: defaultTransport(), + } +} diff --git a/vendor/github.com/fsouza/go-dockerclient/client_unix.go b/vendor/github.com/fsouza/go-dockerclient/client_unix.go index 1bc6af6ed81..b578e03037b 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client_unix.go +++ b/vendor/github.com/fsouza/go-dockerclient/client_unix.go @@ -1,16 +1,15 @@ -// +build !windows // 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 !windows + package docker import ( "context" "net" "net/http" - - "github.com/hashicorp/go-cleanhttp" ) // initializeNativeClient initializes the native Unix domain socket client on @@ -20,9 +19,9 @@ func (c *Client) initializeNativeClient() { return } socketPath := c.endpointURL.Path - tr := cleanhttp.DefaultTransport() + tr := defaultTransport() tr.Dial = func(network, addr string) (net.Conn, error) { - return c.Dialer.Dial(network, addr) + return c.Dialer.Dial(unixProtocol, socketPath) } tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { return c.Dialer.Dial(unixProtocol, socketPath) diff --git a/vendor/github.com/fsouza/go-dockerclient/client_windows.go b/vendor/github.com/fsouza/go-dockerclient/client_windows.go index 95ef56b4c3c..c863fb05faa 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client_windows.go +++ b/vendor/github.com/fsouza/go-dockerclient/client_windows.go @@ -13,7 +13,6 @@ import ( "time" "github.com/Microsoft/go-winio" - "github.com/hashicorp/go-cleanhttp" ) const namedPipeConnectTimeout = 2 * time.Second @@ -36,7 +35,7 @@ func (c *Client) initializeNativeClient() { timeout := namedPipeConnectTimeout return winio.DialPipe(namedPipePath, &timeout) } - tr := cleanhttp.DefaultTransport() + tr := defaultTransport() tr.Dial = dialFunc tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { return dialFunc(network, addr) diff --git a/vendor/github.com/fsouza/go-dockerclient/container.go b/vendor/github.com/fsouza/go-dockerclient/container.go index a6734e3c3a7..9c121095dd0 100644 --- a/vendor/github.com/fsouza/go-dockerclient/container.go +++ b/vendor/github.com/fsouza/go-dockerclient/container.go @@ -204,7 +204,7 @@ func (s *State) StateString() string { // 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" toml:"HostIP,omitempty"` + HostIP string `json:"HostIp,omitempty" yaml:"HostIp,omitempty" toml:"HostIp,omitempty"` HostPort string `json:"HostPort,omitempty" yaml:"HostPort,omitempty" toml:"HostPort,omitempty"` } @@ -214,15 +214,16 @@ 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" toml:"MacAddress,omitempty"` - GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"` - GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"` - IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"` - IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"` - IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"` - Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"` - EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"` - NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"` + Aliases []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty" toml:"Aliases,omitempty"` + MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"` + GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"` + GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"` + IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"` + IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"` + IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"` + Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"` + EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"` + NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"` } // NetworkSettings contains network-related information about a container @@ -327,6 +328,43 @@ type Config struct { VolumesFrom string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty" toml:"VolumesFrom,omitempty"` } +// HostMount represents a mount point in the container in HostConfig. +// +// It has been added in the version 1.25 of the Docker API +type HostMount struct { + Target string `json:"Target,omitempty" yaml:"Target,omitempty" toml:"Target,omitempty"` + Source string `json:"Source,omitempty" yaml:"Source,omitempty" toml:"Source,omitempty"` + Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` + ReadOnly bool `json:"ReadOnly,omitempty" yaml:"ReadOnly,omitempty" toml:"ReadOnly,omitempty"` + BindOptions *BindOptions `json:"BindOptions,omitempty" yaml:"BindOptions,omitempty" toml:"BindOptions,omitempty"` + VolumeOptions *VolumeOptions `json:"VolumeOptions,omitempty" yaml:"VolumeOptions,omitempty" toml:"VolumeOptions,omitempty"` + TempfsOptions *TempfsOptions `json:"TempfsOptions,omitempty" yaml:"TempfsOptions,omitempty" toml:"TempfsOptions,omitempty"` +} + +// BindOptions contains optional configuration for the bind type +type BindOptions struct { + Propagation string `json:"Propagation,omitempty" yaml:"Propagation,omitempty" toml:"Propagation,omitempty"` +} + +// VolumeOptions contains optional configuration for the volume type +type VolumeOptions struct { + NoCopy bool `json:"NoCopy,omitempty" yaml:"NoCopy,omitempty" toml:"NoCopy,omitempty"` + Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` + DriverConfig VolumeDriverConfig `json:"DriverConfig,omitempty" yaml:"DriverConfig,omitempty" toml:"DriverConfig,omitempty"` +} + +// TempfsOptions contains optional configuration for the tempfs type +type TempfsOptions struct { + SizeBytes int64 `json:"SizeBytes,omitempty" yaml:"SizeBytes,omitempty" toml:"SizeBytes,omitempty"` + Mode int `json:"Mode,omitempty" yaml:"Mode,omitempty" toml:"Mode,omitempty"` +} + +// VolumeDriverConfig holds a map of volume driver specific options +type VolumeDriverConfig struct { + Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` + Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"` +} + // Mount represents a mount point in the container. // // It has been added in the version 1.20 of the Docker API, available since @@ -707,7 +745,7 @@ type HostConfig struct { MemoryReservation int64 `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty" toml:"MemoryReservation,omitempty"` KernelMemory int64 `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty" toml:"KernelMemory,omitempty"` MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty" toml:"MemorySwap,omitempty"` - MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty" toml:"MemorySwappiness,omitempty"` + MemorySwappiness int64 `json:"MemorySwappiness" yaml:"MemorySwappiness" toml:"MemorySwappiness"` CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty" toml:"CpuShares,omitempty"` CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty" toml:"Cpuset,omitempty"` CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty" toml:"CpusetCpus,omitempty"` @@ -735,6 +773,11 @@ type HostConfig struct { AutoRemove bool `json:"AutoRemove,omitempty" yaml:"AutoRemove,omitempty" toml:"AutoRemove,omitempty"` StorageOpt map[string]string `json:"StorageOpt,omitempty" yaml:"StorageOpt,omitempty" toml:"StorageOpt,omitempty"` Sysctls map[string]string `json:"Sysctls,omitempty" yaml:"Sysctls,omitempty" toml:"Sysctls,omitempty"` + CPUCount int64 `json:"CpuCount,omitempty" yaml:"CpuCount,omitempty"` + CPUPercent int64 `json:"CpuPercent,omitempty" yaml:"CpuPercent,omitempty"` + IOMaximumBandwidth int64 `json:"IOMaximumBandwidth,omitempty" yaml:"IOMaximumBandwidth,omitempty"` + IOMaximumIOps int64 `json:"IOMaximumIOps,omitempty" yaml:"IOMaximumIOps,omitempty"` + Mounts []HostMount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` } // NetworkingConfig represents the container's networking configuration for each of its interfaces @@ -910,6 +953,8 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) { // See https://goo.gl/Dk3Xio for more details. type Stats struct { Read time.Time `json:"read,omitempty" yaml:"read,omitempty" toml:"read,omitempty"` + PreRead time.Time `json:"preread,omitempty" yaml:"preread,omitempty" toml:"preread,omitempty"` + NumProcs uint32 `json:"num_procs" yaml:"num_procs" toml:"num_procs"` PidsStats struct { Current uint64 `json:"current,omitempty" yaml:"current,omitempty"` } `json:"pids_stats,omitempty" yaml:"pids_stats,omitempty" toml:"pids_stats,omitempty"` @@ -949,10 +994,13 @@ type Stats struct { HierarchicalMemswLimit uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty" toml:"hierarchical_memsw_limit,omitempty"` Swap uint64 `json:"swap,omitempty" yaml:"swap,omitempty" toml:"swap,omitempty"` } `json:"stats,omitempty" yaml:"stats,omitempty" toml:"stats,omitempty"` - MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty" toml:"max_usage,omitempty"` - Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty" toml:"usage,omitempty"` - Failcnt uint64 `json:"failcnt,omitempty" yaml:"failcnt,omitempty" toml:"failcnt,omitempty"` - Limit uint64 `json:"limit,omitempty" yaml:"limit,omitempty" toml:"limit,omitempty"` + MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty" toml:"max_usage,omitempty"` + Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty" toml:"usage,omitempty"` + Failcnt uint64 `json:"failcnt,omitempty" yaml:"failcnt,omitempty" toml:"failcnt,omitempty"` + Limit uint64 `json:"limit,omitempty" yaml:"limit,omitempty" toml:"limit,omitempty"` + Commit uint64 `json:"commitbytes,omitempty" yaml:"commitbytes,omitempty" toml:"privateworkingset,omitempty"` + CommitPeak uint64 `json:"commitpeakbytes,omitempty" yaml:"commitpeakbytes,omitempty" toml:"commitpeakbytes,omitempty"` + PrivateWorkingSet uint64 `json:"privateworkingset,omitempty" yaml:"privateworkingset,omitempty" toml:"privateworkingset,omitempty"` } `json:"memory_stats,omitempty" yaml:"memory_stats,omitempty" toml:"memory_stats,omitempty"` BlkioStats struct { IOServiceBytesRecursive []BlkioStatsEntry `json:"io_service_bytes_recursive,omitempty" yaml:"io_service_bytes_recursive,omitempty" toml:"io_service_bytes_recursive,omitempty"` @@ -964,8 +1012,14 @@ type Stats struct { IOTimeRecursive []BlkioStatsEntry `json:"io_time_recursive,omitempty" yaml:"io_time_recursive,omitempty" toml:"io_time_recursive,omitempty"` SectorsRecursive []BlkioStatsEntry `json:"sectors_recursive,omitempty" yaml:"sectors_recursive,omitempty" toml:"sectors_recursive,omitempty"` } `json:"blkio_stats,omitempty" yaml:"blkio_stats,omitempty" toml:"blkio_stats,omitempty"` - CPUStats CPUStats `json:"cpu_stats,omitempty" yaml:"cpu_stats,omitempty" toml:"cpu_stats,omitempty"` - PreCPUStats CPUStats `json:"precpu_stats,omitempty"` + CPUStats CPUStats `json:"cpu_stats,omitempty" yaml:"cpu_stats,omitempty" toml:"cpu_stats,omitempty"` + PreCPUStats CPUStats `json:"precpu_stats,omitempty"` + StorageStats struct { + ReadCountNormalized uint64 `json:"read_count_normalized,omitempty" yaml:"read_count_normalized,omitempty" toml:"read_count_normalized,omitempty"` + ReadSizeBytes uint64 `json:"read_size_bytes,omitempty" yaml:"read_size_bytes,omitempty" toml:"read_size_bytes,omitempty"` + WriteCountNormalized uint64 `json:"write_count_normalized,omitempty" yaml:"write_count_normalized,omitempty" toml:"write_count_normalized,omitempty"` + WriteSizeBytes uint64 `json:"write_size_bytes,omitempty" yaml:"write_size_bytes,omitempty" toml:"write_size_bytes,omitempty"` + } `json:"storage_stats,omitempty" yaml:"storage_stats,omitempty" toml:"storage_stats,omitempty"` } // NetworkStats is a stats entry for network stats @@ -1051,6 +1105,7 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) { } }() + reqSent := make(chan struct{}) go func() { err := c.stream("GET", fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{ rawJSONStream: true, @@ -1059,6 +1114,7 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) { timeout: opts.Timeout, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, + reqSent: reqSent, }) if err != nil { dockerError, ok := err.(*Error) @@ -1089,6 +1145,7 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) { decoder := json.NewDecoder(readCloser) stats := new(Stats) + <-reqSent for err := decoder.Decode(stats); err != io.EOF; err = decoder.Decode(stats) { if err != nil { return err @@ -1293,7 +1350,8 @@ type CommitContainerOptions struct { Tag string Message string `qs:"comment"` Author string - Run *Config `qs:"-"` + Changes []string `qs:"changes"` + Run *Config `qs:"-"` Context context.Context } @@ -1478,6 +1536,39 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error { }) } +// PruneContainersOptions specify parameters to the PruneContainers function. +// +// See https://goo.gl/wnkgDT for more details. +type PruneContainersOptions struct { + Filters map[string][]string + Context context.Context +} + +// PruneContainersResults specify results from the PruneContainers function. +// +// See https://goo.gl/wnkgDT for more details. +type PruneContainersResults struct { + ContainersDeleted []string + SpaceReclaimed int64 +} + +// PruneContainers deletes containers which are stopped. +// +// See https://goo.gl/wnkgDT for more details. +func (c *Client) PruneContainers(opts PruneContainersOptions) (*PruneContainersResults, error) { + path := "/containers/prune?" + queryString(opts) + resp, err := c.do("POST", path, doOptions{context: opts.Context}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var results PruneContainersResults + if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { + return nil, err + } + return &results, nil +} + // NoSuchContainer is the error returned when a given container does not exist. type NoSuchContainer struct { ID string diff --git a/vendor/github.com/fsouza/go-dockerclient/event.go b/vendor/github.com/fsouza/go-dockerclient/event.go index 007d2b22bc4..21c0584f05b 100644 --- a/vendor/github.com/fsouza/go-dockerclient/event.go +++ b/vendor/github.com/fsouza/go-dockerclient/event.go @@ -216,7 +216,7 @@ func (eventState *eventMonitoringState) monitorEvents(c *Client) { return } eventState.updateLastSeen(ev) - go eventState.sendEvent(ev) + eventState.sendEvent(ev) case err = <-eventState.errC: if err == ErrNoListeners { eventState.disableEventMonitoring() @@ -274,7 +274,10 @@ func (eventState *eventMonitoringState) sendEvent(event *APIEvents) { } for _, listener := range eventState.listeners { - listener <- event + select { + case listener <- event: + default: + } } } } @@ -342,11 +345,12 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan if event.Time == 0 { continue } - if !c.eventMonitor.isEnabled() || c.eventMonitor.C != eventChan { - return - } transformEvent(&event) - eventChan <- &event + c.eventMonitor.RLock() + if c.eventMonitor.enabled && c.eventMonitor.C == eventChan { + eventChan <- &event + } + c.eventMonitor.RUnlock() } }(res, conn) return nil diff --git a/vendor/github.com/fsouza/go-dockerclient/exec.go b/vendor/github.com/fsouza/go-dockerclient/exec.go index 4b1c48637e2..0048153096b 100644 --- a/vendor/github.com/fsouza/go-dockerclient/exec.go +++ b/vendor/github.com/fsouza/go-dockerclient/exec.go @@ -6,6 +6,7 @@ package docker import ( "encoding/json" + "errors" "fmt" "io" "net/http" @@ -29,6 +30,7 @@ type CreateExecOptions struct { AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"` AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"` Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"` + Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty" toml:"Cmd,omitempty"` Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"` User string `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"` @@ -41,6 +43,9 @@ type CreateExecOptions struct { // // See https://goo.gl/60TeBP for more details func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) { + if len(opts.Env) > 0 && c.serverAPIVersion.LessThan(apiVersion125) { + return nil, errors.New("exec configuration Env is only supported in API#1.25 and above") + } path := fmt.Sprintf("/containers/%s/exec", opts.Container) resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context}) if err != nil { @@ -174,7 +179,9 @@ type ExecInspect struct { OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty" toml:"OpenStderr,omitempty"` OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty" toml:"OpenStdout,omitempty"` ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty" toml:"ProcessConfig,omitempty"` - Container Container `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"` + ContainerID string `json:"ContainerID,omitempty" yaml:"ContainerID,omitempty" toml:"ContainerID,omitempty"` + DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"` + CanRemove bool `json:"CanRemove,omitempty" yaml:"CanRemove,omitempty" toml:"CanRemove,omitempty"` } // InspectExec returns low-level information about the exec command id. diff --git a/vendor/github.com/fsouza/go-dockerclient/image.go b/vendor/github.com/fsouza/go-dockerclient/image.go index 7e61d662a14..c386ad5daea 100644 --- a/vendor/github.com/fsouza/go-dockerclient/image.go +++ b/vendor/github.com/fsouza/go-dockerclient/image.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "os" + "strings" "time" "golang.org/x/net/context" @@ -54,6 +55,7 @@ type Image struct { VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"` RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"` RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty" toml:"RootFS,omitempty"` + OS string `json:"Os,omitempty" yaml:"Os,omitempty" toml:"Os,omitempty"` } // ImagePre012 serves the same purpose as the Image type except that it is for @@ -313,6 +315,11 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error if err != nil { return err } + if opts.Tag == "" && strings.Contains(opts.Repository, "@") { + parts := strings.SplitN(opts.Repository, "@", 2) + opts.Repository = parts[0] + opts.Tag = parts[1] + } return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context) } @@ -333,8 +340,9 @@ func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, // // See https://goo.gl/rEsBV3 for more details. type LoadImageOptions struct { - InputStream io.Reader - Context context.Context + InputStream io.Reader + OutputStream io.Writer + Context context.Context } // LoadImage imports a tarball docker image @@ -344,6 +352,7 @@ func (c *Client) LoadImage(opts LoadImageOptions) error { return c.stream("POST", "/images/load", streamOptions{ setRawTerminal: true, in: opts.InputStream, + stdout: opts.OutputStream, context: opts.Context, }) } @@ -440,6 +449,7 @@ type BuildImageOptions struct { Name string `qs:"t"` Dockerfile string `qs:"dockerfile"` NoCache bool `qs:"nocache"` + CacheFrom []string `qs:"-"` SuppressOutput bool `qs:"q"` Pull bool `qs:"pull"` RmTmpContainer bool `qs:"rm"` @@ -462,6 +472,7 @@ type BuildImageOptions struct { BuildArgs []BuildArg `qs:"-"` NetworkMode string `qs:"networkmode"` InactivityTimeout time.Duration `qs:"-"` + CgroupParent string `qs:"cgroupparent"` Context context.Context } @@ -505,8 +516,16 @@ func (c *Client) BuildImage(opts BuildImageOptions) error { return err } } - qs := queryString(&opts) + + if c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion125) && len(opts.CacheFrom) > 0 { + if b, err := json.Marshal(opts.CacheFrom); err == nil { + item := url.Values(map[string][]string{}) + item.Add("cachefrom", string(b)) + qs = fmt.Sprintf("%s&%s", qs, item.Encode()) + } + } + if len(opts.Ulimits) > 0 { if b, err := json.Marshal(opts.Ulimits); err == nil { item := url.Values(map[string][]string{}) @@ -665,3 +684,36 @@ func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImage return searchResult, nil } + +// PruneImagesOptions specify parameters to the PruneImages function. +// +// See https://goo.gl/qfZlbZ for more details. +type PruneImagesOptions struct { + Filters map[string][]string + Context context.Context +} + +// PruneImagesResults specify results from the PruneImages function. +// +// See https://goo.gl/qfZlbZ for more details. +type PruneImagesResults struct { + ImagesDeleted []struct{ Untagged, Deleted string } + SpaceReclaimed int64 +} + +// PruneImages deletes images which are unused. +// +// See https://goo.gl/qfZlbZ for more details. +func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) { + path := "/images/prune?" + queryString(opts) + resp, err := c.do("POST", path, doOptions{context: opts.Context}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var results PruneImagesResults + if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { + return nil, err + } + return &results, nil +} diff --git a/vendor/github.com/fsouza/go-dockerclient/misc.go b/vendor/github.com/fsouza/go-dockerclient/misc.go index aef595fe1ee..0482838abee 100644 --- a/vendor/github.com/fsouza/go-dockerclient/misc.go +++ b/vendor/github.com/fsouza/go-dockerclient/misc.go @@ -79,6 +79,10 @@ type DockerInfo struct { ServerVersion string ClusterStore string ClusterAdvertise string + Isolation string + InitBinary string + DefaultRuntime string + LiveRestoreEnabled bool Swarm swarm.Info } diff --git a/vendor/github.com/fsouza/go-dockerclient/network.go b/vendor/github.com/fsouza/go-dockerclient/network.go index 691bbe4119e..295efd565dd 100644 --- a/vendor/github.com/fsouza/go-dockerclient/network.go +++ b/vendor/github.com/fsouza/go-dockerclient/network.go @@ -268,6 +268,38 @@ func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) err return nil } +// PruneNetworksOptions specify parameters to the PruneNetworks function. +// +// See https://goo.gl/kX0S9h for more details. +type PruneNetworksOptions struct { + Filters map[string][]string + Context context.Context +} + +// PruneNetworksResults specify results from the PruneNetworks function. +// +// See https://goo.gl/kX0S9h for more details. +type PruneNetworksResults struct { + NetworksDeleted []string +} + +// PruneNetworks deletes networks which are unused. +// +// See https://goo.gl/kX0S9h for more details. +func (c *Client) PruneNetworks(opts PruneNetworksOptions) (*PruneNetworksResults, error) { + path := "/networks/prune?" + queryString(opts) + resp, err := c.do("POST", path, doOptions{context: opts.Context}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var results PruneNetworksResults + if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { + return nil, err + } + return &results, nil +} + // NoSuchNetwork is the error returned when a given network does not exist. type NoSuchNetwork struct { ID string diff --git a/vendor/github.com/fsouza/go-dockerclient/service.go b/vendor/github.com/fsouza/go-dockerclient/service.go index 3daf59c5d47..33af547c6e5 100644 --- a/vendor/github.com/fsouza/go-dockerclient/service.go +++ b/vendor/github.com/fsouza/go-dockerclient/service.go @@ -6,9 +6,11 @@ package docker import ( "encoding/json" + "io" "net/http" "net/url" "strconv" + "time" "github.com/docker/docker/api/types/swarm" "golang.org/x/net/context" @@ -31,6 +33,7 @@ func (err *NoSuchService) Error() string { // // See https://goo.gl/KrVjHz for more details. type CreateServiceOptions struct { + Auth AuthConfiguration `qs:"-"` swarm.ServiceSpec Context context.Context } @@ -40,8 +43,13 @@ type CreateServiceOptions struct { // // See https://goo.gl/KrVjHz for more details. func (c *Client) CreateService(opts CreateServiceOptions) (*swarm.Service, error) { + headers, err := headersWithAuth(opts.Auth) + if err != nil { + return nil, err + } path := "/services/create?" + queryString(opts) resp, err := c.do("POST", path, doOptions{ + headers: headers, data: opts.ServiceSpec, forceJSON: true, context: opts.Context, @@ -85,6 +93,7 @@ func (c *Client) RemoveService(opts RemoveServiceOptions) error { // // See https://goo.gl/wu3MmS for more details. type UpdateServiceOptions struct { + Auth AuthConfiguration `qs:"-"` swarm.ServiceSpec Context context.Context Version uint64 @@ -94,9 +103,14 @@ type UpdateServiceOptions struct { // // See https://goo.gl/wu3MmS for more details. func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error { + headers, err := headersWithAuth(opts.Auth) + if err != nil { + return err + } params := make(url.Values) params.Set("version", strconv.FormatUint(opts.Version, 10)) resp, err := c.do("POST", "/services/"+id+"/update?"+params.Encode(), doOptions{ + headers: headers, data: opts.ServiceSpec, forceJSON: true, context: opts.Context, @@ -155,3 +169,48 @@ func (c *Client) ListServices(opts ListServicesOptions) ([]swarm.Service, error) } return services, nil } + +// LogsServiceOptions represents the set of options used when getting logs from a +// service. +type LogsServiceOptions struct { + Context context.Context + Service string `qs:"-"` + OutputStream io.Writer `qs:"-"` + ErrorStream io.Writer `qs:"-"` + InactivityTimeout time.Duration `qs:"-"` + Tail string + + // Use raw terminal? Usually true when the container contains a TTY. + RawTerminal bool `qs:"-"` + Since int64 + Follow bool + Stdout bool + Stderr bool + Timestamps bool + Details bool +} + +// GetServiceLogs gets stdout and stderr logs from the specified service. +// +// When LogsServiceOptions.RawTerminal is set to false, go-dockerclient will multiplex +// the streams and send the containers stdout to LogsServiceOptions.OutputStream, and +// stderr to LogsServiceOptions.ErrorStream. +// +// When LogsServiceOptions.RawTerminal is true, callers will get the raw stream on +// LogsServiceOptions.OutputStream. +func (c *Client) GetServiceLogs(opts LogsServiceOptions) error { + if opts.Service == "" { + return &NoSuchService{ID: opts.Service} + } + if opts.Tail == "" { + opts.Tail = "all" + } + path := "/services/" + opts.Service + "/logs?" + queryString(opts) + return c.stream("GET", path, streamOptions{ + setRawTerminal: opts.RawTerminal, + stdout: opts.OutputStream, + stderr: opts.ErrorStream, + inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, + }) +} diff --git a/vendor/github.com/fsouza/go-dockerclient/swarm.go b/vendor/github.com/fsouza/go-dockerclient/swarm.go index ba4981aacbc..6d9086a5522 100644 --- a/vendor/github.com/fsouza/go-dockerclient/swarm.go +++ b/vendor/github.com/fsouza/go-dockerclient/swarm.go @@ -33,7 +33,7 @@ type InitSwarmOptions struct { } // InitSwarm initializes a new Swarm and returns the node ID. -// See https://goo.gl/hzkgWu for more details. +// See https://goo.gl/ZWyG1M for more details. func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) { path := "/swarm/init" resp, err := c.do("POST", path, doOptions{ @@ -42,7 +42,7 @@ func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) { context: opts.Context, }) if err != nil { - if e, ok := err.(*Error); ok && e.Status == http.StatusNotAcceptable { + if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return "", ErrNodeAlreadyInSwarm } return "", err @@ -63,7 +63,7 @@ type JoinSwarmOptions struct { } // JoinSwarm joins an existing Swarm. -// See https://goo.gl/TdhJWU for more details. +// See https://goo.gl/N59IP1 for more details. func (c *Client) JoinSwarm(opts JoinSwarmOptions) error { path := "/swarm/join" resp, err := c.do("POST", path, doOptions{ @@ -72,7 +72,7 @@ func (c *Client) JoinSwarm(opts JoinSwarmOptions) error { context: opts.Context, }) if err != nil { - if e, ok := err.(*Error); ok && e.Status == http.StatusNotAcceptable { + if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return ErrNodeAlreadyInSwarm } } @@ -88,7 +88,7 @@ type LeaveSwarmOptions struct { } // LeaveSwarm leaves a Swarm. -// See https://goo.gl/UWDlLg for more details. +// See https://goo.gl/FTX1aD for more details. func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error { params := make(url.Values) params.Set("force", strconv.FormatBool(opts.Force)) @@ -97,7 +97,7 @@ func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error { context: opts.Context, }) if err != nil { - if e, ok := err.(*Error); ok && e.Status == http.StatusNotAcceptable { + if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return ErrNodeNotInSwarm } } @@ -116,7 +116,7 @@ type UpdateSwarmOptions struct { } // UpdateSwarm updates a Swarm. -// See https://goo.gl/vFbq36 for more details. +// See https://goo.gl/iJFnsw for more details. func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error { params := make(url.Values) params.Set("version", strconv.Itoa(opts.Version)) @@ -129,7 +129,7 @@ func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error { context: opts.Context, }) if err != nil { - if e, ok := err.(*Error); ok && e.Status == http.StatusNotAcceptable { + if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return ErrNodeNotInSwarm } } @@ -138,13 +138,16 @@ func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error { } // InspectSwarm inspects a Swarm. -// See http://goo.gl/nvwytL for more details. +// See https://goo.gl/MFwgX9 for more details. func (c *Client) InspectSwarm(ctx context.Context) (swarm.Swarm, error) { response := swarm.Swarm{} resp, err := c.do("GET", "/swarm", doOptions{ context: ctx, }) if err != nil { + if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { + return response, ErrNodeNotInSwarm + } return response, err } defer resp.Body.Close() diff --git a/vendor/github.com/fsouza/go-dockerclient/volume.go b/vendor/github.com/fsouza/go-dockerclient/volume.go index 635ac143498..3c7bdeaa747 100644 --- a/vendor/github.com/fsouza/go-dockerclient/volume.go +++ b/vendor/github.com/fsouza/go-dockerclient/volume.go @@ -136,3 +136,36 @@ func (c *Client) RemoveVolume(name string) error { defer resp.Body.Close() return nil } + +// PruneVolumesOptions specify parameters to the PruneVolumes function. +// +// See https://goo.gl/pFN1Hj for more details. +type PruneVolumesOptions struct { + Filters map[string][]string + Context context.Context +} + +// PruneVolumesResults specify results from the PruneVolumes function. +// +// See https://goo.gl/pFN1Hj for more details. +type PruneVolumesResults struct { + VolumesDeleted []string + SpaceReclaimed int64 +} + +// PruneVolumes deletes volumes which are unused. +// +// See https://goo.gl/pFN1Hj for more details. +func (c *Client) PruneVolumes(opts PruneVolumesOptions) (*PruneVolumesResults, error) { + path := "/volumes/prune?" + queryString(opts) + resp, err := c.do("POST", path, doOptions{context: opts.Context}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var results PruneVolumesResults + if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { + return nil, err + } + return &results, nil +}