diff --git a/vendor/github.com/fsouza/go-dockerclient/auth.go b/vendor/github.com/fsouza/go-dockerclient/auth.go index 95596d7827..5ba9468410 100644 --- a/vendor/github.com/fsouza/go-dockerclient/auth.go +++ b/vendor/github.com/fsouza/go-dockerclient/auth.go @@ -46,23 +46,47 @@ type dockerConfig struct { Email string `json:"email"` } -// NewAuthConfigurationsFromDockerCfg returns AuthConfigurations from the -// ~/.dockercfg file. -func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) { - var r io.Reader - var err error - p := path.Join(os.Getenv("HOME"), ".docker", "config.json") - r, err = os.Open(p) +// NewAuthConfigurationsFromFile returns AuthConfigurations from a path containing JSON +// in the same format as the .dockercfg file. +func NewAuthConfigurationsFromFile(path string) (*AuthConfigurations, error) { + r, err := os.Open(path) if err != nil { - p := path.Join(os.Getenv("HOME"), ".dockercfg") - r, err = os.Open(p) - if err != nil { - return nil, err - } + return nil, err } return NewAuthConfigurations(r) } +func cfgPaths(dockerConfigEnv string, homeEnv string) []string { + var paths []string + if dockerConfigEnv != "" { + paths = append(paths, path.Join(dockerConfigEnv, "config.json")) + } + if homeEnv != "" { + paths = append(paths, path.Join(homeEnv, ".docker", "config.json")) + paths = append(paths, path.Join(homeEnv, ".dockercfg")) + } + return paths +} + +// NewAuthConfigurationsFromDockerCfg returns AuthConfigurations from +// system config files. The following files are checked in the order listed: +// - $DOCKER_CONFIG/config.json if DOCKER_CONFIG set in the environment, +// - $HOME/.docker/config.json +// - $HOME/.dockercfg +func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) { + err := fmt.Errorf("No docker configuration found") + var auths *AuthConfigurations + + pathsToTry := cfgPaths(os.Getenv("DOCKER_CONFIG"), os.Getenv("HOME")) + for _, path := range pathsToTry { + auths, err = NewAuthConfigurationsFromFile(path) + if err == nil { + return auths, nil + } + } + return auths, err +} + // NewAuthConfigurations returns AuthConfigurations from a JSON encoded string in the // same format as the .dockercfg file. func NewAuthConfigurations(r io.Reader) (*AuthConfigurations, error) { diff --git a/vendor/github.com/fsouza/go-dockerclient/cancelable.go b/vendor/github.com/fsouza/go-dockerclient/cancelable.go deleted file mode 100644 index 375fbd15c3..0000000000 --- a/vendor/github.com/fsouza/go-dockerclient/cancelable.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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) - } -} diff --git a/vendor/github.com/fsouza/go-dockerclient/cancelable_go14.go b/vendor/github.com/fsouza/go-dockerclient/cancelable_go14.go deleted file mode 100644 index 3c203986fc..0000000000 --- a/vendor/github.com/fsouza/go-dockerclient/cancelable_go14.go +++ /dev/null @@ -1,19 +0,0 @@ -// 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) - } - } -} diff --git a/vendor/github.com/fsouza/go-dockerclient/client.go b/vendor/github.com/fsouza/go-dockerclient/client.go index 394115731d..d621e7b29b 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client.go +++ b/vendor/github.com/fsouza/go-dockerclient/client.go @@ -34,6 +34,8 @@ import ( "github.com/docker/docker/pkg/homedir" "github.com/docker/docker/pkg/stdcopy" "github.com/hashicorp/go-cleanhttp" + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" ) const userAgent = "go-dockerclient" @@ -49,8 +51,8 @@ var ( ErrInactivityTimeout = errors.New("inactivity time exceeded timeout") apiVersion112, _ = NewAPIVersion("1.12") - apiVersion119, _ = NewAPIVersion("1.19") + apiVersion124, _ = NewAPIVersion("1.24") ) // APIVersion is an internal representation of a version of the Remote API. @@ -144,6 +146,9 @@ type Client struct { serverAPIVersion APIVersion expectedAPIVersion APIVersion unixHTTPClient *http.Client + + // A timeout to use when using both the unixHTTPClient and HTTPClient + timeout time.Duration } // NewClient returns a Client instance ready for communication with the given @@ -316,6 +321,12 @@ func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, }, nil } +// SetTimeout takes a timeout and applies it to subsequent requests to the +// docker engine +func (c *Client) SetTimeout(t time.Duration) { + c.timeout = t +} + func (c *Client) checkAPIVersion() error { serverAPIVersionString, err := c.getServerAPIVersionString() if err != nil { @@ -379,6 +390,7 @@ type doOptions struct { data interface{} forceJSON bool headers map[string]string + context context.Context } func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, error) { @@ -405,6 +417,12 @@ func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, e } else { u = c.getURL(path) } + + // If the user has provided a timeout, apply it. + if c.timeout != 0 { + httpClient.Timeout = c.timeout + } + req, err := http.NewRequest(method, u, params) if err != nil { return nil, err @@ -419,12 +437,19 @@ func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, e for k, v := range doOptions.headers { req.Header.Set(k, v) } - resp, err := httpClient.Do(req) + + ctx := doOptions.context + if ctx == nil { + ctx = context.Background() + } + + resp, err := ctxhttp.Do(ctx, httpClient, req) if err != nil { if strings.Contains(err.Error(), "connection refused") { return nil, ErrConnectionRefused } - return nil, err + + return nil, chooseError(ctx, err) } if resp.StatusCode < 200 || resp.StatusCode >= 400 { return nil, newError(resp) @@ -445,6 +470,17 @@ type streamOptions struct { // Timeout with no data is received, it's reset every time new data // arrives inactivityTimeout time.Duration + context context.Context +} + +// if error in context, return that instead of generic http error +func chooseError(ctx context.Context, err error) error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + return err + } } func (c *Client) stream(method, path string, streamOptions streamOptions) error { @@ -477,18 +513,30 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error if streamOptions.stderr == nil { streamOptions.stderr = ioutil.Discard } - cancelRequest := cancelable(c.HTTPClient, req) + + // make a sub-context so that our active cancellation does not affect parent + ctx := streamOptions.context + if ctx == nil { + ctx = context.Background() + } + subCtx, cancelRequest := context.WithCancel(ctx) + defer cancelRequest() + if protocol == "unix" { dial, err := c.Dialer.Dial(protocol, address) if err != nil { return err } - cancelRequest = func() { dial.Close() } - defer dial.Close() + go func() { + select { + case <-subCtx.Done(): + dial.Close() + } + }() breader := bufio.NewReader(dial) err = req.Write(dial) if err != nil { - return err + return chooseError(subCtx, err) } // ReadResponse may hang if server does not replay @@ -504,14 +552,15 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error if strings.Contains(err.Error(), "connection refused") { return ErrConnectionRefused } - return err + + return chooseError(subCtx, err) } } else { - if resp, err = c.HTTPClient.Do(req); err != nil { + if resp, err = ctxhttp.Do(subCtx, c.HTTPClient, req); err != nil { if strings.Contains(err.Error(), "connection refused") { return ErrConnectionRefused } - return err + return chooseError(subCtx, err) } } defer resp.Body.Close() @@ -528,7 +577,7 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error if atomic.LoadUint32(&canceled) != 0 { return ErrInactivityTimeout } - return err + return chooseError(subCtx, err) } return nil } @@ -676,7 +725,7 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (Close } } - errs := make(chan error) + errs := make(chan error, 1) quit := make(chan struct{}) go func() { clientconn := httputil.NewClientConn(dial, nil) @@ -690,7 +739,7 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (Close defer rwc.Close() errChanOut := make(chan error, 1) - errChanIn := make(chan error, 1) + errChanIn := make(chan error, 2) if hijackOptions.stdout == nil && hijackOptions.stderr == nil { close(errChanOut) } else { @@ -740,14 +789,12 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (Close select { case errIn = <-errChanIn: case <-quit: - return } var errOut error select { case errOut = <-errChanOut: case <-quit: - return } if errIn != nil { diff --git a/vendor/github.com/fsouza/go-dockerclient/container.go b/vendor/github.com/fsouza/go-dockerclient/container.go index 81f0168326..ce141aeb57 100644 --- a/vendor/github.com/fsouza/go-dockerclient/container.go +++ b/vendor/github.com/fsouza/go-dockerclient/container.go @@ -16,6 +16,7 @@ import ( "time" "github.com/docker/go-units" + "golang.org/x/net/context" ) // ErrContainerAlreadyExists is the error returned by CreateContainer when the @@ -32,6 +33,7 @@ type ListContainersOptions struct { Since string Before string Filters map[string][]string + Context context.Context } // APIPort is a type that represents a port mapping returned by the Docker API @@ -82,7 +84,7 @@ type NetworkList struct { // 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{}) + resp, err := c.do("GET", path, doOptions{context: opts.Context}) if err != nil { return nil, err } @@ -282,12 +284,15 @@ type Config struct { 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"` + PublishService string `json:"PublishService,omitempty" yaml:"PublishService,omitempty"` + ArgsEscaped bool `json:"ArgsEscaped,omitempty" yaml:"ArgsEscaped,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"` + Healthcheck *HealthConfig `json:"Healthcheck,omitempty" yaml:"Healthcheck,omitempty"` 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"` @@ -347,6 +352,29 @@ type GraphDriver struct { Data map[string]string `json:"Data,omitempty" yaml:"Data,omitempty"` } +// HealthConfig holds configuration settings for the HEALTHCHECK feature +// +// It has been added in the version 1.24 of the Docker API, available since +// Docker 1.12. +type HealthConfig struct { + // Test is the test to perform to check that the container is healthy. + // An empty slice means to inherit the default. + // The options are: + // {} : inherit healthcheck + // {"NONE"} : disable healthcheck + // {"CMD", args...} : exec arguments directly + // {"CMD-SHELL", command} : run command with system's default shell + Test []string `json:"Test,omitempty" yaml:"Test,omitempty"` + + // Zero means to inherit. Durations are expressed as integer nanoseconds. + Interval time.Duration `json:"Interval,omitempty" yaml:"Interval,omitempty"` // Interval is the time to wait between checks. + Timeout time.Duration `json:"Timeout,omitempty" yaml:"Timeout,omitempty"` // Timeout is the time to wait before considering the check to have hung. + + // Retries is the number of consecutive failures needed to consider a container as unhealthy. + // Zero means inherit. + Retries int `json:"Retries,omitempty" yaml:"Retries,omitempty"` +} + // Container is the type encompasing everything about a container - its config, // hostconfig, etc. type Container struct { @@ -400,13 +428,18 @@ type UpdateContainerOptions struct { MemoryReservation int `json:"MemoryReservation"` KernelMemory int `json:"KernelMemory"` RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty"` + Context context.Context } // 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}) + resp, err := c.do("POST", fmt.Sprintf("/containers/"+id+"/update"), doOptions{ + data: opts, + forceJSON: true, + context: opts.Context, + }) if err != nil { return err } @@ -422,14 +455,17 @@ type RenameContainerOptions struct { ID string `qs:"-"` // New name - Name string `json:"name,omitempty" yaml:"name,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Context context.Context } // 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{}) + resp, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{ + context: opts.Context, + }) if err != nil { return err } @@ -485,6 +521,7 @@ type CreateContainerOptions struct { Config *Config `qs:"-"` HostConfig *HostConfig `qs:"-"` NetworkingConfig *NetworkingConfig `qs:"-"` + Context context.Context } // CreateContainer creates a new container, returning the container instance, @@ -506,6 +543,7 @@ func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error opts.HostConfig, opts.NetworkingConfig, }, + context: opts.Context, }, ) @@ -632,43 +670,61 @@ type HostConfig struct { LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"` ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"` SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"` + Cgroup string `json:"Cgroup,omitempty" yaml:"Cgroup,omitempty"` CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"` Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"` MemoryReservation int64 `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty"` + KernelMemory int64 `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty"` MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"` MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"` - OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"` + OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable,omitempty"` 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"` + BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight,omitempty"` + BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice,omitempty"` + BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps,omitempty"` + BlkioDeviceReadIOps []BlockLimit `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps,omitempty"` + BlkioDeviceWriteBps []BlockLimit `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps,omitempty"` + BlkioDeviceWriteIOps []BlockLimit `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps,omitempty"` Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"` VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"` OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"` PidsLimit int64 `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty"` ShmSize int64 `json:"ShmSize,omitempty" yaml:"ShmSize,omitempty"` + Tmpfs map[string]string `json:"Tmpfs,omitempty" yaml:"Tmpfs,omitempty"` + AutoRemove bool `json:"AutoRemove,omitempty" yaml:"AutoRemove,omitempty"` + StorageOpt map[string]string `json:"StorageOpt,omitempty" yaml:"StorageOpt,omitempty"` + Sysctls map[string]string `json:"Sysctls,omitempty" yaml:"Sysctls,omitempty"` } // NetworkingConfig represents the container's networking configuration for each of its interfaces // Carries the networking configs specified in the `docker run` and `docker network connect` commands type NetworkingConfig struct { - EndpointsConfig map[string]*EndpointConfig // Endpoint configs for each connecting network + EndpointsConfig map[string]*EndpointConfig `json:"EndpointsConfig" yaml:"EndpointsConfig"` // Endpoint configs for each connecting network } // StartContainer starts a container, returning an error in case of failure. // +// Passing the HostConfig to this method has been deprecated in Docker API 1.22 +// (Docker Engine 1.10.x) and totally removed in Docker API 1.24 (Docker Engine +// 1.12.x). The client will ignore the parameter when communicating with Docker +// API 1.24 or greater. +// // See https://goo.gl/MrBAJv for more details. func (c *Client) StartContainer(id string, hostConfig *HostConfig) error { + var opts doOptions path := "/containers/" + id + "/start" - resp, err := c.do("POST", path, doOptions{data: hostConfig, forceJSON: true}) + if c.serverAPIVersion == nil { + c.checkAPIVersion() + } + if c.serverAPIVersion != nil && c.serverAPIVersion.LessThan(apiVersion124) { + opts = doOptions{data: hostConfig, forceJSON: true} + } + resp, err := c.do("POST", path, opts) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: id, Err: err} @@ -897,6 +953,7 @@ type StatsOptions struct { // Timeout with no data is received, it's reset every time new data // arrives InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // Stats sends container statistics for the given container to the given channel. @@ -936,6 +993,7 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) { stdout: writeCloser, timeout: opts.Timeout, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) if err != nil { dockerError, ok := err.(*Error) @@ -986,7 +1044,8 @@ type KillContainerOptions struct { // The signal to send to the container. When omitted, Docker server // will assume SIGKILL. - Signal Signal + Signal Signal + Context context.Context } // KillContainer sends a signal to a container, returning an error in case of @@ -995,7 +1054,7 @@ type KillContainerOptions struct { // 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{}) + resp, err := c.do("POST", path, doOptions{context: opts.Context}) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: opts.ID} @@ -1019,7 +1078,8 @@ type RemoveContainerOptions struct { // A flag that indicates whether Docker should remove the container // even if it is currently running. - Force bool + Force bool + Context context.Context } // RemoveContainer removes a container, returning an error in case of failure. @@ -1027,7 +1087,7 @@ type RemoveContainerOptions struct { // 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{}) + resp, err := c.do("DELETE", path, doOptions{context: opts.Context}) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: opts.ID} @@ -1046,6 +1106,7 @@ type UploadToContainerOptions struct { InputStream io.Reader `json:"-" qs:"-"` Path string `qs:"path"` NoOverwriteDirNonDir bool `qs:"noOverwriteDirNonDir"` + Context context.Context } // UploadToContainer uploads a tar archive to be extracted to a path in the @@ -1056,7 +1117,8 @@ func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) err url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts) return c.stream("PUT", url, streamOptions{ - in: opts.InputStream, + in: opts.InputStream, + context: opts.Context, }) } @@ -1068,6 +1130,7 @@ type DownloadFromContainerOptions struct { OutputStream io.Writer `json:"-" qs:"-"` Path string `qs:"path"` InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // DownloadFromContainer downloads a tar archive of files or folders in a container. @@ -1080,6 +1143,7 @@ func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOpti setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) } @@ -1090,6 +1154,7 @@ type CopyFromContainerOptions struct { OutputStream io.Writer `json:"-"` Container string `json:"-"` Resource string + Context context.Context `json:"-"` } // CopyFromContainer has been DEPRECATED, please use DownloadFromContainerOptions along with DownloadFromContainer. @@ -1100,7 +1165,10 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error { return &NoSuchContainer{ID: opts.Container} } url := fmt.Sprintf("/containers/%s/copy", opts.Container) - resp, err := c.do("POST", url, doOptions{data: opts}) + resp, err := c.do("POST", url, doOptions{ + data: opts, + context: opts.Context, + }) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: opts.Container} @@ -1142,6 +1210,7 @@ type CommitContainerOptions struct { Message string `qs:"comment"` Author string Run *Config `qs:"-"` + Context context.Context } // CommitContainer creates a new image from a container's changes. @@ -1149,7 +1218,10 @@ type CommitContainerOptions struct { // 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}) + resp, err := c.do("POST", path, doOptions{ + data: opts.Run, + context: opts.Context, + }) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return nil, &NoSuchContainer{ID: opts.Container} @@ -1247,6 +1319,7 @@ type LogsOptions struct { // Use raw terminal? Usually true when the container contains a TTY. RawTerminal bool `qs:"-"` + Context context.Context } // Logs gets stdout and stderr logs from the specified container. @@ -1265,6 +1338,7 @@ func (c *Client) Logs(opts LogsOptions) error { stdout: opts.OutputStream, stderr: opts.ErrorStream, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) } @@ -1291,6 +1365,7 @@ type ExportContainerOptions struct { ID string OutputStream io.Writer InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // ExportContainer export the contents of container id as tar archive @@ -1306,6 +1381,7 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error { setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) } diff --git a/vendor/github.com/fsouza/go-dockerclient/event.go b/vendor/github.com/fsouza/go-dockerclient/event.go index 10cc52c0b2..8868c0cf8c 100644 --- a/vendor/github.com/fsouza/go-dockerclient/event.go +++ b/vendor/github.com/fsouza/go-dockerclient/event.go @@ -109,7 +109,7 @@ func (c *Client) RemoveEventListener(listener chan *APIEvents) error { if err != nil { return err } - if len(c.eventMonitor.listeners) == 0 { + if c.eventMonitor.listernersCount() == 0 { c.eventMonitor.disableEventMonitoring() } return nil @@ -150,6 +150,12 @@ func (eventState *eventMonitoringState) closeListeners() { eventState.listeners = nil } +func (eventState *eventMonitoringState) listernersCount() int { + eventState.RLock() + defer eventState.RUnlock() + return len(eventState.listeners) +} + func listenerExists(a chan<- *APIEvents, list *[]chan<- *APIEvents) bool { for _, b := range *list { if b == a { diff --git a/vendor/github.com/fsouza/go-dockerclient/exec.go b/vendor/github.com/fsouza/go-dockerclient/exec.go index 1a16da9d62..dab0dfc41a 100644 --- a/vendor/github.com/fsouza/go-dockerclient/exec.go +++ b/vendor/github.com/fsouza/go-dockerclient/exec.go @@ -11,6 +11,8 @@ import ( "net/http" "net/url" "strconv" + + "golang.org/x/net/context" ) // Exec is the type representing a `docker exec` instance and containing the @@ -23,13 +25,14 @@ type Exec struct { // // See https://goo.gl/1KSIb7 for more details type CreateExecOptions struct { - AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"` - AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"` - AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"` - Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"` - Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty"` - Container string `json:"Container,omitempty" yaml:"Container,omitempty"` - User string `json:"User,omitempty" yaml:"User,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"` + Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"` + Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty"` + Container string `json:"Container,omitempty" yaml:"Container,omitempty"` + User string `json:"User,omitempty" yaml:"User,omitempty"` + Context context.Context `json:"-"` } // CreateExec sets up an exec instance in a running container `id`, returning the exec @@ -38,7 +41,7 @@ type CreateExecOptions struct { // See https://goo.gl/1KSIb7 for more details func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) { path := fmt.Sprintf("/containers/%s/exec", opts.Container) - resp, err := c.do("POST", path, doOptions{data: opts}) + resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context}) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return nil, &NoSuchContainer{ID: opts.Container} @@ -75,6 +78,8 @@ type StartExecOptions struct { // It must be an unbuffered channel. Using a buffered channel can lead // to unexpected behavior. Success chan struct{} `json:"-"` + + Context context.Context `json:"-"` } // StartExec starts a previously set up exec instance id. If opts.Detach is @@ -106,7 +111,7 @@ func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWa path := fmt.Sprintf("/exec/%s/start", id) if opts.Detach { - resp, err := c.do("POST", path, doOptions{data: opts}) + resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context}) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return nil, &NoSuchExec{ID: id} diff --git a/vendor/github.com/fsouza/go-dockerclient/image.go b/vendor/github.com/fsouza/go-dockerclient/image.go index fd51c3f928..c1aeda8de0 100644 --- a/vendor/github.com/fsouza/go-dockerclient/image.go +++ b/vendor/github.com/fsouza/go-dockerclient/image.go @@ -15,6 +15,8 @@ import ( "net/url" "os" "time" + + "golang.org/x/net/context" ) // APIImages represent an image returned in the ListImages call. @@ -99,6 +101,7 @@ type ListImagesOptions struct { Filters map[string][]string Digests bool Filter string + Context context.Context } // ListImages returns the list of available images in the server. @@ -106,7 +109,7 @@ type ListImagesOptions struct { // See https://goo.gl/xBe1u3 for more details. func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) { path := "/images/json?" + queryString(opts) - resp, err := c.do("GET", path, doOptions{}) + resp, err := c.do("GET", path, doOptions{context: opts.Context}) if err != nil { return nil, err } @@ -169,6 +172,7 @@ func (c *Client) RemoveImage(name string) error { type RemoveImageOptions struct { Force bool `qs:"force"` NoPrune bool `qs:"noprune"` + Context context.Context } // RemoveImageExtended removes an image by its name or ID. @@ -177,7 +181,7 @@ type RemoveImageOptions struct { // See https://goo.gl/V3ZWnK for more details. func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error { uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts)) - resp, err := c.do("DELETE", uri, doOptions{}) + resp, err := c.do("DELETE", uri, doOptions{context: opts.Context}) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return ErrNoSuchImage @@ -246,6 +250,8 @@ type PushImageOptions struct { OutputStream io.Writer `qs:"-"` RawJSONStream bool `qs:"-"` InactivityTimeout time.Duration `qs:"-"` + + Context context.Context } // PushImage pushes an image to a remote registry, logging progress to w. @@ -271,6 +277,7 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error headers: headers, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) } @@ -280,12 +287,17 @@ 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 + // Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21 + // and Docker Engine < 1.9 + // This parameter was removed in Docker Engine 1.11 + Registry string + OutputStream io.Writer `qs:"-"` RawJSONStream bool `qs:"-"` InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // PullImage pulls an image from a remote registry, logging progress to @@ -301,10 +313,10 @@ 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, opts.InactivityTimeout) + return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context) } -func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration) error { +func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error { path := "/images/create?" + qs return c.stream("POST", path, streamOptions{ setRawTerminal: true, @@ -313,6 +325,7 @@ func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, stdout: w, rawJSONStream: rawJSONStream, inactivityTimeout: timeout, + context: context, }) } @@ -321,6 +334,7 @@ func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, // See https://goo.gl/JyClMX for more details. type LoadImageOptions struct { InputStream io.Reader + Context context.Context } // LoadImage imports a tarball docker image @@ -330,6 +344,7 @@ func (c *Client) LoadImage(opts LoadImageOptions) error { return c.stream("POST", "/images/load", streamOptions{ setRawTerminal: true, in: opts.InputStream, + context: opts.Context, }) } @@ -339,7 +354,8 @@ func (c *Client) LoadImage(opts LoadImageOptions) error { type ExportImageOptions struct { Name string OutputStream io.Writer - InactivityTimeout time.Duration `qs:"-"` + InactivityTimeout time.Duration + Context context.Context } // ExportImage exports an image (as a tar file) into the stream. @@ -350,6 +366,7 @@ func (c *Client) ExportImage(opts ExportImageOptions) error { setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) } @@ -360,6 +377,7 @@ type ExportImagesOptions struct { Names []string OutputStream io.Writer `qs:"-"` InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // ExportImages exports one or more images (as a tar file) into the stream @@ -389,6 +407,7 @@ type ImportImageOptions struct { OutputStream io.Writer `qs:"-"` RawJSONStream bool `qs:"-"` InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // ImportImage imports an image from a url, a file or stdin @@ -409,7 +428,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, opts.InactivityTimeout) + return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context) } // BuildImageOptions present the set of informations available for building an @@ -441,6 +460,7 @@ type BuildImageOptions struct { Ulimits []ULimit `qs:"-"` BuildArgs []BuildArg `qs:"-"` InactivityTimeout time.Duration `qs:"-"` + Context context.Context } // BuildArg represents arguments that can be passed to the image when building @@ -512,6 +532,7 @@ func (c *Client) BuildImage(opts BuildImageOptions) error { in: opts.InputStream, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, + context: opts.Context, }) } @@ -529,9 +550,10 @@ func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) interface{ // // See https://goo.gl/98ZzkU for more details. type TagImageOptions struct { - Repo string - Tag string - Force bool + Repo string + Tag string + Force bool + Context context.Context } // TagImage adds a tag to the image identified by the given name. @@ -541,8 +563,9 @@ func (c *Client) TagImage(name string, opts TagImageOptions) error { if name == "" { return ErrNoSuchImage } - resp, err := c.do("POST", fmt.Sprintf("/images/"+name+"/tag?%s", - queryString(&opts)), doOptions{}) + resp, err := c.do("POST", "/images/"+name+"/tag?"+queryString(&opts), doOptions{ + context: opts.Context, + }) if err != nil { return err diff --git a/vendor/github.com/fsouza/go-dockerclient/network.go b/vendor/github.com/fsouza/go-dockerclient/network.go index 07a1a5796d..9f5dbdd930 100644 --- a/vendor/github.com/fsouza/go-dockerclient/network.go +++ b/vendor/github.com/fsouza/go-dockerclient/network.go @@ -10,6 +10,8 @@ import ( "errors" "fmt" "net/http" + + "golang.org/x/net/context" ) // ErrNetworkAlreadyExists is the error returned by CreateNetwork when the @@ -108,21 +110,23 @@ func (c *Client) NetworkInfo(id string) (*Network, error) { // // See https://goo.gl/6GugX3 for more details. type CreateNetworkOptions struct { - Name string `json:"Name"` - CheckDuplicate bool `json:"CheckDuplicate"` - Driver string `json:"Driver"` - IPAM IPAMOptions `json:"IPAM"` - Options map[string]interface{} `json:"Options"` - Internal bool `json:"Internal"` - EnableIPv6 bool `json:"EnableIPv6"` + Name string `json:"Name" yaml:"Name"` + CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate"` + Driver string `json:"Driver" yaml:"Driver"` + IPAM IPAMOptions `json:"IPAM" yaml:"IPAM"` + Options map[string]interface{} `json:"Options" yaml:"Options"` + Label map[string]string `json:"Labels" yaml:"Labels"` + Internal bool `json:"Internal" yaml:"Internal"` + EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6"` + Context context.Context `json:"-"` } // IPAMOptions controls IP Address Management when creating a network // // See https://goo.gl/T8kRVH for more details. type IPAMOptions struct { - Driver string `json:"Driver"` - Config []IPAMConfig `json:"Config"` + Driver string `json:"Driver" yaml:"Driver"` + Config []IPAMConfig `json:"Config" yaml:"Config"` } // IPAMConfig represents IPAM configurations @@ -144,7 +148,8 @@ func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) { "POST", "/networks/create", doOptions{ - data: opts, + data: opts, + context: opts.Context, }, ) if err != nil { @@ -200,24 +205,26 @@ type NetworkConnectionOptions struct { // Force is only applicable to the DisconnectNetwork call Force bool + + Context context.Context `json:"-"` } // EndpointConfig stores network endpoint details // // See https://goo.gl/RV7BJU for more details. type EndpointConfig struct { - IPAMConfig *EndpointIPAMConfig - Links []string - Aliases []string - NetworkID string - EndpointID string - Gateway string - IPAddress string - IPPrefixLen int - IPv6Gateway string - GlobalIPv6Address string - GlobalIPv6PrefixLen int - MacAddress string + IPAMConfig *EndpointIPAMConfig `json:"IPAMConfig,omitempty" yaml:"IPAMConfig,omitempty"` + Links []string `json:"Links,omitempty" yaml:"Links,omitempty"` + Aliases []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty"` + NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"` + EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"` + Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"` + IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"` + IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty"` + IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty"` + GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty"` + GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty"` + MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"` } // EndpointIPAMConfig represents IPAM configurations for an @@ -234,7 +241,10 @@ type EndpointIPAMConfig struct { // // See https://goo.gl/6GugX3 for more details. func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error { - resp, err := c.do("POST", "/networks/"+id+"/connect", doOptions{data: opts}) + resp, err := c.do("POST", "/networks/"+id+"/connect", doOptions{ + data: opts, + context: opts.Context, + }) if err != nil { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container} diff --git a/vendor/github.com/fsouza/go-dockerclient/testing/server.go b/vendor/github.com/fsouza/go-dockerclient/testing/server.go index a9d0c6d881..2807d881eb 100644 --- a/vendor/github.com/fsouza/go-dockerclient/testing/server.go +++ b/vendor/github.com/fsouza/go-dockerclient/testing/server.go @@ -9,9 +9,12 @@ package testing import ( "archive/tar" "crypto/rand" + "crypto/tls" + "crypto/x509" "encoding/json" "errors" "fmt" + "io" "io/ioutil" mathrand "math/rand" "net" @@ -66,6 +69,22 @@ type volumeCounter struct { count int } +func buildDockerServer(listener net.Listener, containerChan chan<- *docker.Container, hook func(*http.Request)) *DockerServer { + server := DockerServer{ + listener: listener, + imgIDs: make(map[string]string), + hook: hook, + failures: make(map[string]string), + execCallbacks: make(map[string]func()), + statsCallbacks: make(map[string]func(string) docker.Stats), + customHandlers: make(map[string]http.Handler), + uploadedFiles: make(map[string]string), + cChan: containerChan, + } + server.buildMuxer() + return &server +} + // NewServer returns a new instance of the fake server, in standalone mode. Use // the method URL to get the URL of the server. // @@ -82,20 +101,41 @@ func NewServer(bind string, containerChan chan<- *docker.Container, hook func(*h if err != nil { return nil, err } - server := DockerServer{ - listener: listener, - imgIDs: make(map[string]string), - hook: hook, - failures: make(map[string]string), - execCallbacks: make(map[string]func()), - statsCallbacks: make(map[string]func(string) docker.Stats), - customHandlers: make(map[string]http.Handler), - uploadedFiles: make(map[string]string), - cChan: containerChan, + server := buildDockerServer(listener, containerChan, hook) + go http.Serve(listener, server) + return server, nil +} + +type TLSConfig struct { + CertPath string + CertKeyPath string + RootCAPath string +} + +func NewTLSServer(bind string, containerChan chan<- *docker.Container, hook func(*http.Request), tlsConfig TLSConfig) (*DockerServer, error) { + listener, err := net.Listen("tcp", bind) + if err != nil { + return nil, err } - server.buildMuxer() - go http.Serve(listener, &server) - return &server, nil + defaultCertificate, err := tls.LoadX509KeyPair(tlsConfig.CertPath, tlsConfig.CertKeyPath) + if err != nil { + return nil, err + } + tlsServerConfig := new(tls.Config) + tlsServerConfig.Certificates = []tls.Certificate{defaultCertificate} + if tlsConfig.RootCAPath != "" { + rootCertPEM, err := ioutil.ReadFile(tlsConfig.RootCAPath) + if err != nil { + return nil, err + } + certsPool := x509.NewCertPool() + certsPool.AppendCertsFromPEM(rootCertPEM) + tlsServerConfig.RootCAs = certsPool + } + tlsListener := tls.NewListener(listener, tlsServerConfig) + server := buildDockerServer(tlsListener, containerChan, hook) + go http.Serve(tlsListener, server) + return server, nil } func (s *DockerServer) notify(container *docker.Container) { @@ -145,6 +185,7 @@ func (s *DockerServer) buildMuxer() { s.mux.Path("/volumes/{name:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectVolume)) s.mux.Path("/volumes/{name:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeVolume)) s.mux.Path("/info").Methods("GET").HandlerFunc(s.handlerWrapper(s.infoDocker)) + s.mux.Path("/version").Methods("GET").HandlerFunc(s.handlerWrapper(s.versionDocker)) } // SetHook changes the hook function used by the server. @@ -557,14 +598,22 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) { s.cMut.Lock() defer s.cMut.Unlock() defer r.Body.Close() - var hostConfig docker.HostConfig + if container.State.Running { + http.Error(w, "", http.StatusNotModified) + return + } + var hostConfig *docker.HostConfig err = json.NewDecoder(r.Body).Decode(&hostConfig) - if err != nil { + if err != nil && err != io.EOF { http.Error(w, err.Error(), http.StatusInternalServerError) return } - container.HostConfig = &hostConfig - if len(hostConfig.PortBindings) > 0 { + if hostConfig == nil { + hostConfig = container.HostConfig + } else { + container.HostConfig = hostConfig + } + if hostConfig != nil && len(hostConfig.PortBindings) > 0 { ports := map[docker.Port][]docker.PortBinding{} for key, items := range hostConfig.PortBindings { bindings := make([]docker.PortBinding, len(items)) @@ -585,10 +634,6 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) { } container.NetworkSettings.Ports = ports } - if container.State.Running { - http.Error(w, "", http.StatusNotModified) - return - } container.State.Running = true s.notify(container) } @@ -1332,3 +1377,19 @@ func (s *DockerServer) infoDocker(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(envs) } + +func (s *DockerServer) versionDocker(w http.ResponseWriter, r *http.Request) { + envs := map[string]interface{}{ + "Version": "1.10.1", + "Os": "linux", + "KernelVersion": "3.13.0-77-generic", + "GoVersion": "go1.4.2", + "GitCommit": "9e83765", + "Arch": "amd64", + "ApiVersion": "1.22", + "BuildTime": "2015-12-01T07:09:13.444803460+00:00", + "Experimental": false, + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(envs) +} diff --git a/vendor/github.com/fsouza/go-dockerclient/volume.go b/vendor/github.com/fsouza/go-dockerclient/volume.go index 5fe8ee3d63..331541f89a 100644 --- a/vendor/github.com/fsouza/go-dockerclient/volume.go +++ b/vendor/github.com/fsouza/go-dockerclient/volume.go @@ -8,6 +8,8 @@ import ( "encoding/json" "errors" "net/http" + + "golang.org/x/net/context" ) var ( @@ -33,13 +35,16 @@ type Volume struct { // See https://goo.gl/FZA4BK for more details. type ListVolumesOptions struct { Filters map[string][]string + Context context.Context } // ListVolumes returns a list of available volumes in the server. // // See https://goo.gl/FZA4BK for more details. func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) { - resp, err := c.do("GET", "/volumes?"+queryString(opts), doOptions{}) + resp, err := c.do("GET", "/volumes?"+queryString(opts), doOptions{ + context: opts.Context, + }) if err != nil { return nil, err } @@ -70,13 +75,17 @@ type CreateVolumeOptions struct { Name string Driver string DriverOpts map[string]string + Context context.Context `json:"-"` } // CreateVolume creates a volume on the server. // // See https://goo.gl/pBUbZ9 for more details. func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) { - resp, err := c.do("POST", "/volumes/create", doOptions{data: opts}) + resp, err := c.do("POST", "/volumes/create", doOptions{ + data: opts, + context: opts.Context, + }) if err != nil { return nil, err } diff --git a/vendor/manifest b/vendor/manifest index 9d33dc8f2a..2013387506 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -753,7 +753,7 @@ "importpath": "github.com/fsouza/go-dockerclient", "repository": "https://github.com/fsouza/go-dockerclient", "vcs": "git", - "revision": "3c8f092cb1e9d1e18a07c1d05d993e69a6676097", + "revision": "4a2e5288244cd60febbfc7bdd07891eab9efa6c1", "branch": "master", "notests": true },