vendors: update fsouza/go-docker-client to v.1.6.3

This commit is contained in:
Mahmood Ali 2020-03-30 15:10:53 -04:00
parent 4d90bf3699
commit 8f57f78087
45 changed files with 690 additions and 1706 deletions

View File

@ -719,7 +719,7 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T
StorageOpt: driverConfig.StorageOpt,
VolumeDriver: driverConfig.VolumeDriver,
PidsLimit: driverConfig.PidsLimit,
PidsLimit: &driverConfig.PidsLimit,
}
if _, ok := task.DeviceEnv[nvidiaVisibleDevices]; ok {
@ -747,8 +747,10 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T
// Windows does not support MemorySwap/MemorySwappiness #2193
if runtime.GOOS == "windows" {
var swapiness int64 = 0
hostConfig.MemorySwap = 0
hostConfig.MemorySwappiness = -1
hostConfig.MemorySwappiness = &swapiness
} else {
hostConfig.MemorySwap = task.Resources.LinuxResources.MemoryLimitBytes // MemorySwap is memory + swap.
}

View File

@ -15,6 +15,7 @@ Andrews Medina
Andrey Sibiryov
Andy Goldstein
Anirudh Aithal
Antoine Brechon
Antonio Murdaca
Artem Sidorenko
Arthur Rodrigues
@ -51,6 +52,7 @@ Damien Lespiau
Damon Wang
Dan Williams
Daniel, Dao Quang Minh
Daniel Black
Daniel Garcia
Daniel Hess
Daniel Hiltgen
@ -117,6 +119,7 @@ Kevin Xu
Kim, Hirokuni
Kostas Lekkas
Kyle Allan
Kyle Quest
Yunhee Lee
Liron Levin
Lior Yankovich
@ -136,6 +139,7 @@ Michal Fojtik
Mike Dillon
Mrunal Patel
Nate Jones
Nathan Pemberton
Nguyen Sy Thanh Son
Nicholas Van Wiggeren
Nick Ethier
@ -185,6 +189,7 @@ Tim Schindler
Timothy St. Clair
Tobi Knaup
Tom Wilkie
Tomas Knappek
Tonic
ttyh061
upccup

View File

@ -1,28 +0,0 @@
[[constraint]]
name = "github.com/Microsoft/go-winio"
version = "v0.4.5"
[[constraint]]
branch = "master"
name = "github.com/docker/docker"
[[constraint]]
name = "github.com/docker/go-units"
version = "v0.3.2"
[[constraint]]
name = "github.com/google/go-cmp"
version = "v0.2.0"
[[constraint]]
name = "github.com/gorilla/mux"
version = "v1.5.0"
[[override]]
name = "github.com/Nvveen/Gotty"
source = "https://github.com/ijc25/Gotty.git"
revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c"
[[override]]
name = "github.com/docker/libnetwork"
revision = "19279f0492417475b6bfbd0aa529f73e8f178fb5"

View File

@ -1,4 +1,4 @@
Copyright (c) 2013-2018, go-dockerclient authors
Copyright (c) 2013-2020, go-dockerclient authors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,8 +1,6 @@
.PHONY: \
all \
lint \
vet \
fmtcheck \
pretest \
test \
integration
@ -10,23 +8,13 @@
all: test
lint:
@ go get -v golang.org/x/lint/golint
[ -z "$$(golint . | grep -v 'type name will be used as docker.DockerInfo' | grep -v 'context.Context should be the first' | tee /dev/stderr)" ]
cd /tmp && GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run
vet:
go vet ./...
fmtcheck:
if [ -z "$${SKIP_FMT_CHECK}" ]; then [ -z "$$(gofmt -s -d *.go ./testing | tee /dev/stderr)" ]; fi
testdeps:
go get -u github.com/golang/dep/cmd/dep
dep ensure -v
pretest: testdeps lint vet fmtcheck
pretest: lint
gotest:
go test -race ./...
go test -race -vet all ./...
test: pretest gotest

View File

@ -1,19 +1,36 @@
# go-dockerclient
[![Travis Build Status](https://travis-ci.org/fsouza/go-dockerclient.svg?branch=master)](https://travis-ci.org/fsouza/go-dockerclient)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/4m374pti06ubg2l7?svg=true)](https://ci.appveyor.com/project/fsouza/go-dockerclient)
[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://godoc.org/github.com/fsouza/go-dockerclient)
[![Travis Build Status](https://travis-ci.com/fsouza/go-dockerclient.svg?branch=master)](https://travis-ci.com/fsouza/go-dockerclient)
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/4yusq1f9dqbicobt?svg=true)](https://ci.appveyor.com/project/fsouza/go-dockerclient)
[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/docker/docker/api/types?tab=doc#AuthConfig)
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/).
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
only available in docker 1.8 and above, and only enabled in docker if
DOCKER_EXPERIMENTAL is defined during the docker build process.
passthrough to the libnetwork remote API.
For more details, check the [remote API
documentation](http://docs.docker.com/engine/reference/api/docker_remote_api/).
documentation](https://docs.docker.com/engine/api/latest/).
## Difference between go-dockerclient and the official SDK
Link for the official SDK: https://docs.docker.com/develop/sdk/
go-dockerclient was created before Docker had an official Go SDK and is
still maintained and active because it's still used out there. New features in
the Docker API do not get automatically implemented here: it's based on demand,
if someone wants it, they can file an issue or a PR and the feature may get
implemented/merged.
For new projects, using the official SDK is probably more appropriate as
go-dockerclient lags behind the official SDK.
When using the official SDK, keep in mind that because of how the its
dependencies are organized, you may need some extra steps in order to be able
to import it in your projects (see
[#784](https://github.com/fsouza/go-dockerclient/issues/784) and
[moby/moby#28269](https://github.com/moby/moby/issues/28269)).
## Example
@ -23,12 +40,11 @@ package main
import (
"fmt"
"github.com/fsouza/go-dockerclient"
docker "github.com/fsouza/go-dockerclient"
)
func main() {
endpoint := "unix:///var/run/docker.sock"
client, err := docker.NewClient(endpoint)
client, err := docker.NewClientFromEnv()
if err != nil {
panic(err)
}
@ -59,11 +75,11 @@ package main
import (
"fmt"
"github.com/fsouza/go-dockerclient"
docker "github.com/fsouza/go-dockerclient"
)
func main() {
endpoint := "tcp://[ip]:[port]"
const endpoint = "tcp://[ip]:[port]"
path := os.Getenv("DOCKER_CERT_PATH")
ca := fmt.Sprintf("%s/ca.pem", path)
cert := fmt.Sprintf("%s/cert.pem", path)
@ -75,7 +91,8 @@ func main() {
If using [docker-machine](https://docs.docker.com/machine/), or another
application that exports environment variables `DOCKER_HOST`,
`DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`, you can use NewClientFromEnv.
`DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`, `DOCKER_API_VERSION`, you can use
NewClientFromEnv.
```go
@ -84,11 +101,14 @@ package main
import (
"fmt"
"github.com/fsouza/go-dockerclient"
docker "github.com/fsouza/go-dockerclient"
)
func main() {
client, _ := docker.NewClientFromEnv()
client, err := docker.NewClientFromEnv()
if err != nil {
// handle err
}
// use client
}
```
@ -101,22 +121,21 @@ 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://golang.org/cmd/vet/)
* [gofmt](https://golang.org/cmd/gofmt)
* [golangci-lint](https://github.com/golangci/golangci-lint)
* [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.
Running ``make test`` will run all checks, as well as install any required
dependencies.
## Vendoring
## Modules
go-dockerclient uses [dep](https://github.com/golang/dep/) for vendoring. If
you're using dep, you should be able to pick go-dockerclient releases and get
the proper dependencies.
go-dockerclient supports Go modules.
With other vendoring tools, users might need to specify go-dockerclient's
If you're using dep, you can check the [releases
page](https://github.com/fsouza/go-dockerclient/releases) for the latest
release fully compatible with dep.
With other vendoring tools, users need to specify go-dockerclient's
dependencies manually.
## Using with Docker 1.9 and Go 1.4

View File

@ -1,21 +1,24 @@
version: '{build}'
version: "{build}"
platform: x64
clone_depth: 2
clone_folder: c:\gopath\src\github.com\fsouza\go-dockerclient
environment:
GOPATH: c:\gopath
GOPROXY: https://proxy.golang.org
GO111MODULE: on
matrix:
- GOVERSION: 1.10.5
- GOVERSION: 1.11.2
- GOVERSION: "1.12.17"
- GOVERSION: "1.13.8"
- GOVERSION: "1.14rc1"
install:
- choco install make
- 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 -u github.com/golang/dep/cmd/dep
- dep ensure -v
- make pretest
test_script:
- for /f "" %%G in ('go list ./... ^| find /i /v "/vendor/"') do ( go test %%G & IF ERRORLEVEL == 1 EXIT 1)
- make gotest
matrix:
fast_finish: true

View File

@ -12,13 +12,14 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
)
// ErrCannotParseDockercfg is the error returned by NewAuthConfigurations when the dockercfg cannot be parsed.
var ErrCannotParseDockercfg = errors.New("Failed to read authentication from dockercfg")
var ErrCannotParseDockercfg = errors.New("failed to read authentication from dockercfg")
// AuthConfiguration represents authentication options to use in the PushImage
// method. It represents the authentication in the Docker index server.
@ -29,7 +30,7 @@ type AuthConfiguration struct {
ServerAddress string `json:"serveraddress,omitempty"`
// IdentityToken can be supplied with the identitytoken response of the AuthCheck call
// see https://godoc.org/github.com/docker/docker/api/types#AuthConfig
// see https://pkg.go.dev/github.com/docker/docker/api/types?tab=doc#AuthConfig
// It can be used in place of password not in conjunction with it
IdentityToken string `json:"identitytoken,omitempty"`
@ -93,9 +94,11 @@ func NewAuthConfigurationsFromFile(path string) (*AuthConfigurations, error) {
func cfgPaths(dockerConfigEnv string, homeEnv string) []string {
var paths []string
if dockerConfigEnv != "" {
paths = append(paths, path.Join(dockerConfigEnv, "plaintext-passwords.json"))
paths = append(paths, path.Join(dockerConfigEnv, "config.json"))
}
if homeEnv != "" {
paths = append(paths, path.Join(homeEnv, ".docker", "plaintext-passwords.json"))
paths = append(paths, path.Join(homeEnv, ".docker", "config.json"))
paths = append(paths, path.Join(homeEnv, ".dockercfg"))
}
@ -108,7 +111,7 @@ func cfgPaths(dockerConfigEnv string, homeEnv string) []string {
// - $HOME/.docker/config.json
// - $HOME/.dockercfg
func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) {
err := fmt.Errorf("No docker configuration found")
err := fmt.Errorf("no docker configuration found")
var auths *AuthConfigurations
pathsToTry := cfgPaths(os.Getenv("DOCKER_CONFIG"), os.Getenv("HOME"))
@ -167,9 +170,14 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
if conf.Auth == "" {
continue
}
// support both padded and unpadded encoding
data, err := base64.StdEncoding.DecodeString(conf.Auth)
if err != nil {
return nil, err
data, err = base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(conf.Auth)
}
if err != nil {
return nil, errors.New("error decoding plaintext credentials")
}
userpass := strings.SplitN(string(data), ":", 2)
@ -217,7 +225,7 @@ func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) {
if conf == nil {
return authStatus, errors.New("conf is nil")
}
resp, err := c.do("POST", "/auth", doOptions{data: conf})
resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf})
if err != nil {
return authStatus, err
}

View File

@ -31,10 +31,9 @@ import (
"sync/atomic"
"time"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/homedir"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stdcopy"
"github.com/fsouza/go-dockerclient/internal/jsonmessage"
)
const (
@ -55,7 +54,9 @@ var (
ErrInactivityTimeout = errors.New("inactivity time exceeded timeout")
apiVersion112, _ = NewAPIVersion("1.12")
apiVersion118, _ = NewAPIVersion("1.18")
apiVersion119, _ = NewAPIVersion("1.19")
apiVersion121, _ = NewAPIVersion("1.21")
apiVersion124, _ = NewAPIVersion("1.24")
apiVersion125, _ = NewAPIVersion("1.25")
apiVersion135, _ = NewAPIVersion("1.35")
@ -70,7 +71,7 @@ type APIVersion []int
// <minor> and <patch> are integer numbers.
func NewAPIVersion(input string) (APIVersion, error) {
if !strings.Contains(input, ".") {
return nil, fmt.Errorf("Unable to parse version %q", input)
return nil, fmt.Errorf("unable to parse version %q", input)
}
raw := strings.Split(input, "-")
arr := strings.Split(raw[0], ".")
@ -79,7 +80,7 @@ func NewAPIVersion(input string) (APIVersion, error) {
for i, val := range arr {
ret[i], err = strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("Unable to parse version %q: %q is not an integer", input, val)
return nil, fmt.Errorf("unable to parse version %q: %q is not an integer", input, val)
}
}
return ret, nil
@ -263,16 +264,19 @@ func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString stri
}
// NewClientFromEnv returns a Client instance ready for communication created from
// Docker's default logic for the environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH.
// Docker's default logic for the environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH,
// and DOCKER_API_VERSION.
//
// See https://github.com/docker/docker/blob/1f963af697e8df3a78217f6fdbf67b8123a7db94/docker/docker.go#L68.
// See https://github.com/docker/compose/blob/81707ef1ad94403789166d2fe042c8a718a4c748/compose/cli/docker_client.py#L7.
// See https://github.com/moby/moby/blob/28d7dba41d0c0d9c7f0dafcc79d3c59f2b3f5dc3/client/options.go#L51
func NewClientFromEnv() (*Client, error) {
client, err := NewVersionedClientFromEnv("")
apiVersionString := os.Getenv("DOCKER_API_VERSION")
client, err := NewVersionedClientFromEnv(apiVersionString)
if err != nil {
return nil, err
}
client.SkipServerVersionCheck = true
client.SkipServerVersionCheck = apiVersionString == ""
return client, nil
}
@ -329,7 +333,7 @@ func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock,
} else {
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caPEMCert) {
return nil, errors.New("Could not add RootCA pem")
return nil, errors.New("could not add RootCA pem")
}
tlsConfig.RootCAs = caPool
}
@ -387,7 +391,7 @@ func (c *Client) Endpoint() string {
//
// See https://goo.gl/wYfgY1 for more details.
func (c *Client) Ping() error {
return c.PingWithContext(nil)
return c.PingWithContext(context.TODO())
}
// PingWithContext pings the docker server
@ -396,7 +400,7 @@ func (c *Client) Ping() error {
// See https://goo.gl/wYfgY1 for more details.
func (c *Client) PingWithContext(ctx context.Context) error {
path := "/_ping"
resp, err := c.do("GET", path, doOptions{context: ctx})
resp, err := c.do(http.MethodGet, path, doOptions{context: ctx})
if err != nil {
return err
}
@ -408,13 +412,13 @@ func (c *Client) PingWithContext(ctx context.Context) error {
}
func (c *Client) getServerAPIVersionString() (version string, err error) {
resp, err := c.do("GET", "/version", doOptions{})
resp, err := c.do(http.MethodGet, "/version", doOptions{})
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", resp.StatusCode)
return "", fmt.Errorf("received unexpected status %d while trying to retrieve the server version", resp.StatusCode)
}
var versionResponse map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&versionResponse); err != nil {
@ -464,7 +468,7 @@ func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, e
req.Header.Set("User-Agent", userAgent)
if doOptions.data != nil {
req.Header.Set("Content-Type", "application/json")
} else if method == "POST" {
} else if method == http.MethodPost {
req.Header.Set("Content-Type", "plain/text")
}
@ -519,7 +523,7 @@ func chooseError(ctx context.Context, err error) error {
}
func (c *Client) stream(method, path string, streamOptions streamOptions) error {
if (method == "POST" || method == "PUT") && streamOptions.in == nil {
if (method == http.MethodPost || method == http.MethodPut) && streamOptions.in == nil {
streamOptions.in = bytes.NewReader(nil)
}
if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
@ -528,12 +532,25 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
return err
}
}
req, err := http.NewRequest(method, c.getURL(path), streamOptions.in)
return c.streamURL(method, c.getURL(path), streamOptions)
}
func (c *Client) streamURL(method, url string, streamOptions streamOptions) error {
if (method == http.MethodPost || method == http.MethodPut) && streamOptions.in == nil {
streamOptions.in = bytes.NewReader(nil)
}
if !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
err := c.checkAPIVersion()
if err != nil {
return err
}
}
req, err := http.NewRequest(method, url, streamOptions.in)
if err != nil {
return err
}
req.Header.Set("User-Agent", userAgent)
if method == "POST" {
if method == http.MethodPost {
req.Header.Set("Content-Type", "plain/text")
}
for key, val := range streamOptions.headers {
@ -592,6 +609,7 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
return chooseError(subCtx, err)
}
defer resp.Body.Close()
} else {
if resp, err = c.HTTPClient.Do(req.WithContext(subCtx)); err != nil {
if strings.Contains(err.Error(), "connection refused") {
@ -599,11 +617,11 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
}
return chooseError(subCtx, err)
}
defer resp.Body.Close()
if streamOptions.reqSent != nil {
close(streamOptions.reqSent)
}
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
return newError(resp)
}
@ -639,11 +657,7 @@ func handleStreamResponse(resp *http.Response, streamOptions *streamOptions) err
_, err = io.Copy(streamOptions.stdout, resp.Body)
return err
}
if st, ok := streamOptions.stdout.(interface {
io.Writer
FD() uintptr
IsTerminal() bool
}); ok {
if st, ok := streamOptions.stdout.(stream); ok {
err = jsonmessage.DisplayJSONMessagesToStream(resp.Body, st, nil)
} else {
err = jsonmessage.DisplayJSONMessagesStream(resp.Body, streamOptions.stdout, 0, false, nil)
@ -651,6 +665,12 @@ func handleStreamResponse(resp *http.Response, streamOptions *streamOptions) err
return err
}
type stream interface {
io.Writer
FD() uintptr
IsTerminal() bool
}
type proxyReader struct {
io.ReadCloser
calls uint64
@ -760,8 +780,10 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (Close
errs := make(chan error, 1)
quit := make(chan struct{})
go func() {
//nolint:staticcheck
clientconn := httputil.NewClientConn(dial, nil)
defer clientconn.Close()
//nolint:bodyclose
clientconn.Do(req)
if hijackOptions.success != nil {
hijackOptions.success <- struct{}{}
@ -856,6 +878,29 @@ func (c *Client) getURL(path string) string {
return fmt.Sprintf("%s%s", urlStr, path)
}
func (c *Client) getPath(basepath string, opts interface{}) (string, error) {
queryStr, requiredAPIVersion := queryStringVersion(opts)
return c.pathVersionCheck(basepath, queryStr, requiredAPIVersion)
}
func (c *Client) pathVersionCheck(basepath, queryStr string, requiredAPIVersion APIVersion) (string, error) {
urlStr := strings.TrimRight(c.endpointURL.String(), "/")
if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol {
urlStr = ""
}
if c.requestedAPIVersion != nil {
if c.requestedAPIVersion.GreaterThanOrEqualTo(requiredAPIVersion) {
return fmt.Sprintf("%s/v%s%s?%s", urlStr, c.requestedAPIVersion, basepath, queryStr), nil
}
return "", fmt.Errorf("API %s requires version %s, requested version %s is insufficient",
basepath, requiredAPIVersion, c.requestedAPIVersion)
}
if requiredAPIVersion != nil {
return fmt.Sprintf("%s/v%s%s?%s", urlStr, requiredAPIVersion, basepath, queryStr), nil
}
return fmt.Sprintf("%s%s?%s", urlStr, basepath, queryStr), nil
}
// getFakeNativeURL returns the URL needed to make an HTTP request over a UNIX
// domain socket to the given path.
func (c *Client) getFakeNativeURL(path string) string {
@ -872,24 +917,18 @@ func (c *Client) getFakeNativeURL(path string) string {
return fmt.Sprintf("%s%s", urlStr, path)
}
type jsonMessage struct {
Status string `json:"status,omitempty"`
Progress string `json:"progress,omitempty"`
Error string `json:"error,omitempty"`
Stream string `json:"stream,omitempty"`
}
func queryString(opts interface{}) string {
func queryStringVersion(opts interface{}) (string, APIVersion) {
if opts == nil {
return ""
return "", nil
}
value := reflect.ValueOf(opts)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
if value.Kind() != reflect.Struct {
return ""
return "", nil
}
var apiVersion APIVersion
items := url.Values(map[string][]string{})
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)
@ -902,53 +941,80 @@ func queryString(opts interface{}) string {
} else if key == "-" {
continue
}
addQueryStringValue(items, key, value.Field(i))
if addQueryStringValue(items, key, value.Field(i)) {
verstr := field.Tag.Get("ver")
if verstr != "" {
ver, _ := NewAPIVersion(verstr)
if apiVersion == nil {
apiVersion = ver
} else if ver.GreaterThan(apiVersion) {
apiVersion = ver
}
}
}
}
return items.Encode()
return items.Encode(), apiVersion
}
func addQueryStringValue(items url.Values, key string, v reflect.Value) {
func queryString(opts interface{}) string {
s, _ := queryStringVersion(opts)
return s
}
func addQueryStringValue(items url.Values, key string, v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
if v.Bool() {
items.Add(key, "1")
return true
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v.Int() > 0 {
items.Add(key, strconv.FormatInt(v.Int(), 10))
return true
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if v.Uint() > 0 {
items.Add(key, strconv.FormatUint(v.Uint(), 10))
return true
}
case reflect.Float32, reflect.Float64:
if v.Float() > 0 {
items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
return true
}
case reflect.String:
if v.String() != "" {
items.Add(key, v.String())
return true
}
case reflect.Ptr:
if !v.IsNil() {
if b, err := json.Marshal(v.Interface()); err == nil {
items.Add(key, string(b))
return true
}
}
case reflect.Map:
if len(v.MapKeys()) > 0 {
if b, err := json.Marshal(v.Interface()); err == nil {
items.Add(key, string(b))
return true
}
}
case reflect.Array, reflect.Slice:
vLen := v.Len()
var valuesAdded int
if vLen > 0 {
for i := 0; i < vLen; i++ {
addQueryStringValue(items, key, v.Index(i))
if addQueryStringValue(items, key, v.Index(i)) {
valuesAdded++
}
}
}
return valuesAdded > 0
}
return false
}
// Error represents failures in the API. It represents a failure from the API.
@ -1029,7 +1095,7 @@ func getDockerEnv() (*dockerEnv, error) {
dockerHost := os.Getenv("DOCKER_HOST")
var err error
if dockerHost == "" {
dockerHost = opts.DefaultHost
dockerHost = defaultHost
}
dockerTLSVerify := os.Getenv("DOCKER_TLS_VERIFY") != ""
var dockerCertPath string

View File

@ -12,6 +12,8 @@ import (
"net/http"
)
const defaultHost = "unix:///var/run/docker.sock"
// initializeNativeClient initializes the native Unix domain socket client on
// Unix-style operating systems
func (c *Client) initializeNativeClient(trFunc func() *http.Transport) {
@ -21,11 +23,8 @@ func (c *Client) initializeNativeClient(trFunc func() *http.Transport) {
sockPath := c.endpointURL.Path
tr := trFunc()
tr.Dial = func(network, addr string) (net.Conn, error) {
return c.Dialer.Dial(unixProtocol, sockPath)
}
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
tr.Proxy = nil
tr.DialContext = func(_ context.Context, network, addr string) (net.Conn, error) {
return c.Dialer.Dial(unixProtocol, sockPath)
}
c.HTTPClient.Transport = tr

View File

@ -2,8 +2,6 @@
// 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 (
@ -12,10 +10,13 @@ import (
"net/http"
"time"
"github.com/Microsoft/go-winio"
winio "github.com/Microsoft/go-winio"
)
const namedPipeConnectTimeout = 2 * time.Second
const (
defaultHost = "npipe:////./pipe/docker_engine"
namedPipeConnectTimeout = 2 * time.Second
)
type pipeDialer struct {
dialFunc func(network, addr string) (net.Conn, error)
@ -31,12 +32,13 @@ func (c *Client) initializeNativeClient(trFunc func() *http.Transport) {
return
}
namedPipePath := c.endpointURL.Path
dialFunc := func(network, addr string) (net.Conn, error) {
//nolint:unparam
dialFunc := func(_, addr string) (net.Conn, error) {
timeout := namedPipeConnectTimeout
return winio.DialPipe(namedPipePath, &timeout)
}
tr := trFunc()
tr.Dial = dialFunc
tr.Proxy = nil
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialFunc(network, addr)
}

View File

@ -16,7 +16,7 @@ import (
"strings"
"time"
"github.com/docker/go-units"
units "github.com/docker/go-units"
)
// ErrContainerAlreadyExists is the error returned by CreateContainer when the
@ -52,7 +52,8 @@ type APIMount struct {
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"`
Mode string `json:"Mode,omitempty" yaml:"Mode,omitempty" toml:"Mode,omitempty"`
RW bool `json:"RW,omitempty" yaml:"RW,omitempty" toml:"RW,omitempty"`
Propogation string `json:"Propogation,omitempty" yaml:"Propogation,omitempty" toml:"Propogation,omitempty"`
Propagation string `json:"Propagation,omitempty" yaml:"Propagation,omitempty" toml:"Propagation,omitempty"`
Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
}
// APIContainers represents each container in the list returned by
@ -84,7 +85,7 @@ type NetworkList struct {
// See https://goo.gl/kaOHGw for more details.
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
path := "/containers/json?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
@ -473,6 +474,12 @@ type Container struct {
RestartCount int `json:"RestartCount,omitempty" yaml:"RestartCount,omitempty" toml:"RestartCount,omitempty"`
AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty" toml:"AppArmorProfile,omitempty"`
MountLabel string `json:"MountLabel,omitempty" yaml:"MountLabel,omitempty" toml:"MountLabel,omitempty"`
ProcessLabel string `json:"ProcessLabel,omitempty" yaml:"ProcessLabel,omitempty" toml:"ProcessLabel,omitempty"`
Platform string `json:"Platform,omitempty" yaml:"Platform,omitempty" toml:"Platform,omitempty"`
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty" toml:"SizeRw,omitempty"`
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty" toml:"SizeRootFs,omitempty"`
}
// UpdateContainerOptions specify parameters to the UpdateContainer function.
@ -499,7 +506,7 @@ type UpdateContainerOptions struct {
//
// 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{
resp, err := c.do(http.MethodPost, fmt.Sprintf("/containers/"+id+"/update"), doOptions{
data: opts,
forceJSON: true,
context: opts.Context,
@ -527,7 +534,7 @@ type RenameContainerOptions struct {
//
// See https://goo.gl/46inai 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(http.MethodPost, fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{
context: opts.Context,
})
if err != nil {
@ -539,25 +546,31 @@ func (c *Client) RenameContainer(opts RenameContainerOptions) error {
// InspectContainer returns information about a container by its ID.
//
// See https://goo.gl/FaI5JT for more details.
// Deprecated: Use InspectContainerWithOptions instead.
func (c *Client) InspectContainer(id string) (*Container, error) {
return c.inspectContainer(id, doOptions{})
return c.InspectContainerWithOptions(InspectContainerOptions{ID: id})
}
// InspectContainerWithContext returns information about a container by its ID.
// The context object can be used to cancel the inspect request.
//
// See https://goo.gl/FaI5JT for more details.
// Deprecated: Use InspectContainerWithOptions instead.
//nolint:golint
func (c *Client) InspectContainerWithContext(id string, ctx context.Context) (*Container, error) {
return c.inspectContainer(id, doOptions{context: ctx})
return c.InspectContainerWithOptions(InspectContainerOptions{ID: id, Context: ctx})
}
func (c *Client) inspectContainer(id string, opts doOptions) (*Container, error) {
path := "/containers/" + id + "/json"
resp, err := c.do("GET", path, opts)
// InspectContainerWithOptions returns information about a container by its ID.
//
// See https://goo.gl/FaI5JT for more details.
func (c *Client) InspectContainerWithOptions(opts InspectContainerOptions) (*Container, error) {
path := "/containers/" + opts.ID + "/json?" + queryString(opts)
resp, err := c.do(http.MethodGet, path, doOptions{
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchContainer{ID: id}
return nil, &NoSuchContainer{ID: opts.ID}
}
return nil, err
}
@ -569,12 +582,21 @@ func (c *Client) inspectContainer(id string, opts doOptions) (*Container, error)
return &container, nil
}
// InspectContainerOptions specifies parameters for InspectContainerWithOptions.
//
// See https://goo.gl/FaI5JT for more details.
type InspectContainerOptions struct {
Context context.Context
ID string `qs:"-"`
Size bool
}
// ContainerChanges returns changes in the filesystem of the given container.
//
// See https://goo.gl/15KKzh for more details.
func (c *Client) ContainerChanges(id string) ([]Change, error) {
path := "/containers/" + id + "/changes"
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchContainer{ID: id}
@ -610,7 +632,7 @@ type CreateContainerOptions struct {
func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
path := "/containers/create?" + queryString(opts)
resp, err := c.do(
"POST",
http.MethodPost,
path,
doOptions{
data: struct {
@ -627,7 +649,7 @@ func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error
)
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
if e.Status == http.StatusNotFound && strings.Contains(e.Message, "No such image") {
return nil, ErrNoSuchImage
}
if e.Status == http.StatusConflict {
@ -708,6 +730,15 @@ type Device struct {
CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty" toml:"CgroupPermissions,omitempty"`
}
// A list of requests for devices to be sent to device drivers
type DeviceRequest struct {
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"`
Count int `json:"Count,omitempty" yaml:"Count,omitempty" toml:"Count,omitempty"`
DeviceIDs []string `json:"DeviceIDs,omitempty" yaml:"DeviceIDs,omitempty" toml:"DeviceIDs,omitempty"`
Capabilities [][]string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"`
Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"`
}
// BlockWeight represents a relative device weight for an individual device inside
// of a container
type BlockWeight struct {
@ -728,6 +759,7 @@ type HostConfig struct {
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty" toml:"Binds,omitempty"`
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty" toml:"CapAdd,omitempty"`
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty" toml:"CapDrop,omitempty"`
Capabilities []string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"` // Mutually exclusive w.r.t. CapAdd and CapDrop API v1.40
GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty" toml:"GroupAdd,omitempty"`
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty" toml:"ContainerIDFile,omitempty"`
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty" toml:"LxcConf,omitempty"`
@ -741,20 +773,23 @@ type HostConfig struct {
UsernsMode string `json:"UsernsMode,omitempty" yaml:"UsernsMode,omitempty" toml:"UsernsMode,omitempty"`
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty" toml:"NetworkMode,omitempty"`
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty" toml:"IpcMode,omitempty"`
Isolation string `json:"Isolation,omitempty" yaml:"Isolation,omitempty" toml:"Isolation,omitempty"` // Windows only
ConsoleSize [2]int `json:"ConsoleSize,omitempty" yaml:"ConsoleSize,omitempty" toml:"ConsoleSize,omitempty"` // Windows only height x width
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty" toml:"PidMode,omitempty"`
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty" toml:"UTSMode,omitempty"`
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty" toml:"RestartPolicy,omitempty"`
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"`
DeviceCgroupRules []string `json:"DeviceCgroupRules,omitempty" yaml:"DeviceCgroupRules,omitempty" toml:"DeviceCgroupRules,omitempty"`
DeviceRequests []DeviceRequest `json:"DeviceRequests,omitempty" yaml:"DeviceRequests,omitempty" toml:"DeviceRequests,omitempty"`
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty" toml:"LogConfig,omitempty"`
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty" toml:"SecurityOpt,omitempty"`
CgroupnsMode string `json:"CgroupnsMode,omitempty" yaml:"CgroupnsMode,omitempty" toml:"CgroupnsMode,omitempty"` // v1.40+
Cgroup string `json:"Cgroup,omitempty" yaml:"Cgroup,omitempty" toml:"Cgroup,omitempty"`
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty" toml:"CgroupParent,omitempty"`
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"`
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"`
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"`
@ -763,6 +798,7 @@ type HostConfig struct {
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty" toml:"CpuPeriod,omitempty"`
CPURealtimePeriod int64 `json:"CpuRealtimePeriod,omitempty" yaml:"CpuRealtimePeriod,omitempty" toml:"CpuRealtimePeriod,omitempty"`
CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime,omitempty" yaml:"CpuRealtimeRuntime,omitempty" toml:"CpuRealtimeRuntime,omitempty"`
NanoCPUs int64 `json:"NanoCpus,omitempty" yaml:"NanoCpus,omitempty" toml:"NanoCpus,omitempty"`
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight,omitempty" toml:"BlkioWeight,omitempty"`
BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice,omitempty" toml:"BlkioWeightDevice,omitempty"`
BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps,omitempty" toml:"BlkioDeviceReadBps,omitempty"`
@ -772,14 +808,11 @@ type HostConfig struct {
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty" toml:"Ulimits,omitempty"`
VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty" toml:"VolumeDriver,omitempty"`
OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty" toml:"OomScoreAdj,omitempty"`
PidsLimit int64 `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty" toml:"PidsLimit,omitempty"`
MemorySwappiness *int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty" toml:"MemorySwappiness,omitempty"`
PidsLimit *int64 `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty" toml:"PidsLimit,omitempty"`
OOMKillDisable *bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable,omitempty" toml:"OomKillDisable,omitempty"`
ShmSize int64 `json:"ShmSize,omitempty" yaml:"ShmSize,omitempty" toml:"ShmSize,omitempty"`
Tmpfs map[string]string `json:"Tmpfs,omitempty" yaml:"Tmpfs,omitempty" toml:"Tmpfs,omitempty"`
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty" toml:"PublishAllPorts,omitempty"`
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty" toml:"ReadonlyRootfs,omitempty"`
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable,omitempty" toml:"OomKillDisable,omitempty"`
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"`
@ -787,8 +820,14 @@ type HostConfig struct {
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"`
Init bool `json:",omitempty" yaml:",omitempty"`
MaskedPaths []string `json:"MaskedPaths,omitempty" yaml:"MaskedPaths,omitempty" toml:"MaskedPaths,omitempty"`
ReadonlyPaths []string `json:"ReadonlyPaths,omitempty" yaml:"ReadonlyPaths,omitempty" toml:"ReadonlyPaths,omitempty"`
Runtime string `json:"Runtime,omitempty" yaml:"Runtime,omitempty" toml:"Runtime,omitempty"`
Init bool `json:",omitempty" yaml:",omitempty"`
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty" toml:"PublishAllPorts,omitempty"`
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty" toml:"ReadonlyRootfs,omitempty"`
AutoRemove bool `json:"AutoRemove,omitempty" yaml:"AutoRemove,omitempty" toml:"AutoRemove,omitempty"`
}
// NetworkingConfig represents the container's networking configuration for each of its interfaces
@ -819,6 +858,7 @@ func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
// API 1.24 or greater.
//
// See https://goo.gl/fbOSZy for more details.
//nolint:golint
func (c *Client) StartContainerWithContext(id string, hostConfig *HostConfig, ctx context.Context) error {
return c.startContainer(id, hostConfig, doOptions{context: ctx})
}
@ -832,7 +872,7 @@ func (c *Client) startContainer(id string, hostConfig *HostConfig, opts doOption
opts.data = hostConfig
opts.forceJSON = true
}
resp, err := c.do("POST", path, opts)
resp, err := c.do(http.MethodPost, path, opts)
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchContainer{ID: id, Err: err}
@ -859,13 +899,14 @@ func (c *Client) StopContainer(id string, timeout uint) error {
// container request.
//
// See https://goo.gl/R9dZcV for more details.
//nolint:golint
func (c *Client) StopContainerWithContext(id string, timeout uint, ctx context.Context) error {
return c.stopContainer(id, timeout, doOptions{context: ctx})
}
func (c *Client) stopContainer(id string, timeout uint, opts doOptions) error {
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
resp, err := c.do("POST", path, opts)
resp, err := c.do(http.MethodPost, path, opts)
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchContainer{ID: id}
@ -885,7 +926,7 @@ func (c *Client) stopContainer(id string, timeout uint, opts doOptions) error {
// See https://goo.gl/MrAKQ5 for more details.
func (c *Client) RestartContainer(id string, timeout uint) error {
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
resp, err := c.do("POST", path, doOptions{})
resp, err := c.do(http.MethodPost, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchContainer{ID: id}
@ -901,7 +942,7 @@ func (c *Client) RestartContainer(id string, timeout uint) error {
// See https://goo.gl/D1Yaii for more details.
func (c *Client) PauseContainer(id string) error {
path := fmt.Sprintf("/containers/%s/pause", id)
resp, err := c.do("POST", path, doOptions{})
resp, err := c.do(http.MethodPost, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchContainer{ID: id}
@ -917,7 +958,7 @@ func (c *Client) PauseContainer(id string) error {
// See https://goo.gl/sZ2faO for more details.
func (c *Client) UnpauseContainer(id string) error {
path := fmt.Sprintf("/containers/%s/unpause", id)
resp, err := c.do("POST", path, doOptions{})
resp, err := c.do(http.MethodPost, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchContainer{ID: id}
@ -947,7 +988,7 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
args = fmt.Sprintf("?ps_args=%s", psArgs)
}
path := fmt.Sprintf("/containers/%s/top%s", id, args)
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return result, &NoSuchContainer{ID: id}
@ -1103,13 +1144,8 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
defer func() {
close(opts.Stats)
select {
case err := <-errC:
if err != nil && retErr == nil {
retErr = err
}
default:
// No errors
if err := <-errC; err != nil && retErr == nil {
retErr = err
}
if err := readCloser.Close(); err != nil && retErr == nil {
@ -1119,7 +1155,8 @@ 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{
defer close(errC)
err := c.stream(http.MethodGet, fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{
rawJSONStream: true,
useJSONDecoder: true,
stdout: writeCloser,
@ -1140,7 +1177,6 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
err = closeErr
}
errC <- err
close(errC)
}()
quit := make(chan struct{})
@ -1188,7 +1224,7 @@ type KillContainerOptions struct {
// See https://goo.gl/JnTxXZ for more details.
func (c *Client) KillContainer(opts KillContainerOptions) error {
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
e, ok := err.(*Error)
if !ok {
@ -1229,7 +1265,7 @@ type RemoveContainerOptions struct {
// See https://goo.gl/hL5IPC for more details.
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
path := "/containers/" + opts.ID + "?" + queryString(opts)
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchContainer{ID: opts.ID}
@ -1258,7 +1294,7 @@ type UploadToContainerOptions struct {
func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) error {
url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
return c.stream("PUT", url, streamOptions{
return c.stream(http.MethodPut, url, streamOptions{
in: opts.InputStream,
context: opts.Context,
})
@ -1281,7 +1317,7 @@ type DownloadFromContainerOptions struct {
func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOptions) error {
url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts)
return c.stream("GET", url, streamOptions{
return c.stream(http.MethodGet, url, streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
@ -1314,7 +1350,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
return errors.New("go-dockerclient: CopyFromContainer is no longer available in Docker >= 1.12, use DownloadFromContainer instead")
}
url := fmt.Sprintf("/containers/%s/copy", opts.Container)
resp, err := c.do("POST", url, doOptions{
resp, err := c.do(http.MethodPost, url, doOptions{
data: opts,
context: opts.Context,
})
@ -1342,12 +1378,13 @@ func (c *Client) WaitContainer(id string) (int, error) {
// inspect request.
//
// See https://goo.gl/4AGweZ for more details.
//nolint:golint
func (c *Client) WaitContainerWithContext(id string, ctx context.Context) (int, error) {
return c.waitContainer(id, doOptions{context: ctx})
}
func (c *Client) waitContainer(id string, opts doOptions) (int, error) {
resp, err := c.do("POST", "/containers/"+id+"/wait", opts)
resp, err := c.do(http.MethodPost, "/containers/"+id+"/wait", opts)
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return 0, &NoSuchContainer{ID: id}
@ -1381,7 +1418,7 @@ type CommitContainerOptions struct {
// See https://goo.gl/CzIguf for more details.
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
path := "/commit?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Run,
context: opts.Context,
})
@ -1416,6 +1453,9 @@ type AttachToContainerOptions struct {
// to unexpected behavior.
Success chan struct{}
// Override the key sequence for detaching a container.
DetachKeys string
// Use raw terminal? Usually true when the container contains a TTY.
RawTerminal bool `qs:"-"`
@ -1455,7 +1495,7 @@ func (c *Client) AttachToContainerNonBlocking(opts AttachToContainerOptions) (Cl
return nil, &NoSuchContainer{ID: opts.Container}
}
path := "/containers/" + opts.Container + "/attach?" + queryString(opts)
return c.hijack("POST", path, hijackOptions{
return c.hijack(http.MethodPost, path, hijackOptions{
success: opts.Success,
setRawTerminal: opts.RawTerminal,
in: opts.InputStream,
@ -1505,7 +1545,7 @@ func (c *Client) Logs(opts LogsOptions) error {
opts.Tail = "all"
}
path := "/containers/" + opts.Container + "/logs?" + queryString(opts)
return c.stream("GET", path, streamOptions{
return c.stream(http.MethodGet, path, streamOptions{
setRawTerminal: opts.RawTerminal,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,
@ -1521,7 +1561,7 @@ func (c *Client) ResizeContainerTTY(id string, height, width int) error {
params := make(url.Values)
params.Set("h", strconv.Itoa(height))
params.Set("w", strconv.Itoa(width))
resp, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), doOptions{})
resp, err := c.do(http.MethodPost, "/containers/"+id+"/resize?"+params.Encode(), doOptions{})
if err != nil {
return err
}
@ -1549,7 +1589,7 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error {
return &NoSuchContainer{ID: opts.ID}
}
url := fmt.Sprintf("/containers/%s/export", opts.ID)
return c.stream("GET", url, streamOptions{
return c.stream(http.MethodGet, url, streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
@ -1578,7 +1618,7 @@ type PruneContainersResults struct {
// 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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -6,6 +6,7 @@ package docker
import (
"encoding/json"
"net/http"
"github.com/docker/docker/api/types/registry"
)
@ -13,7 +14,7 @@ import (
// InspectDistribution returns image digest and platform information by contacting the registry
func (c *Client) InspectDistribution(name string) (*registry.DistributionInspect, error) {
path := "/distribution/" + name + "/json"
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
return nil, err
}

View File

@ -178,7 +178,7 @@ func (eventState *eventMonitoringState) enableEventMonitoring(c *Client) error {
return nil
}
func (eventState *eventMonitoringState) disableEventMonitoring() error {
func (eventState *eventMonitoringState) disableEventMonitoring() {
eventState.Lock()
defer eventState.Unlock()
@ -191,7 +191,6 @@ func (eventState *eventMonitoringState) disableEventMonitoring() error {
close(eventState.C)
close(eventState.errC)
}
return nil
}
func (eventState *eventMonitoringState) monitorEvents(c *Client) {
@ -330,15 +329,18 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan
if err != nil {
return err
}
//nolint:staticcheck
conn := httputil.NewClientConn(dial, nil)
req, err := http.NewRequest("GET", uri, nil)
req, err := http.NewRequest(http.MethodGet, uri, nil)
if err != nil {
return err
}
//nolint:bodyclose
res, err := conn.Do(req)
if err != nil {
return err
}
//nolint:staticcheck
go func(res *http.Response, conn *httputil.ClientConn) {
defer conn.Close()
defer res.Body.Close()

View File

@ -25,16 +25,17 @@ type Exec struct {
//
// See https://goo.gl/60TeBP for more details
type CreateExecOptions struct {
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
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"`
WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty" toml:"WorkingDir,omitempty"`
DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"`
Context context.Context `json:"-"`
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
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"`
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
}
@ -50,7 +51,7 @@ func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
return nil, errors.New("exec configuration WorkingDir is only supported in API#1.35 and above")
}
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
resp, err := c.do(http.MethodPost, 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}
@ -119,7 +120,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, context: opts.Context})
resp, err := c.do(http.MethodPost, 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}
@ -130,7 +131,7 @@ func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWa
return nil, nil
}
return c.hijack("POST", path, hijackOptions{
return c.hijack(http.MethodPost, path, hijackOptions{
success: opts.Success,
setRawTerminal: opts.RawTerminal,
in: opts.InputStream,
@ -151,7 +152,7 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
params.Set("w", strconv.Itoa(width))
path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
resp, err := c.do("POST", path, doOptions{})
resp, err := c.do(http.MethodPost, path, doOptions{})
if err != nil {
return err
}
@ -177,13 +178,13 @@ type ExecProcessConfig struct {
type ExecInspect struct {
ID string `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"`
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"`
ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty" toml:"ProcessConfig,omitempty"`
ContainerID string `json:"ContainerID,omitempty" yaml:"ContainerID,omitempty" toml:"ContainerID,omitempty"`
DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"`
Running bool `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"`
OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"`
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"`
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"`
}
@ -192,7 +193,7 @@ type ExecInspect struct {
// See https://goo.gl/ctMUiW for more details
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
path := fmt.Sprintf("/exec/%s/json", id)
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchExec{ID: id}

View File

@ -1,26 +1,22 @@
module github.com/fsouza/go-dockerclient
go 1.12
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78
github.com/Microsoft/go-winio v0.4.11
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5
github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352 // indirect
github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c // indirect
github.com/containerd/containerd v1.3.0 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.3.3
github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241 // indirect
github.com/gogo/protobuf v1.1.1 // indirect
github.com/google/go-cmp v0.2.0
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/mux v1.6.2
github.com/docker/go-units v0.4.0
github.com/google/go-cmp v0.4.0
github.com/gorilla/mux v1.7.4
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/pkg/errors v0.8.0 // indirect
github.com/sirupsen/logrus v1.1.0
github.com/vishvananda/netlink v1.0.0 // indirect
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 // indirect
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611
gotest.tools v2.1.0+incompatible // indirect
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
google.golang.org/grpc v1.22.0 // indirect
)

View File

@ -1,54 +1,137 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352 h1:CdBoaTKPl60tksFVWYc5QnLWwXBcU+XcLiXx8+NcZ9o=
github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c h1:YMP6olTU903X3gxQJckdmiP8/zkSMq4kN3uipsU9XjU=
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY=
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23 h1:Zl/9mUfPbYbnv895OXx9WfxPjwqSZHohuZzVcjJ5QPQ=
github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23 h1:oqgGT9O61YAYvI41EBsLePOr+LE6roB0xY4gpkZuFSE=
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241 h1:+ebE/hCU02srkeIg8Vp/vlUp182JapYWtXzV+bCeR2I=
github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.1.0 h1:65VZabgUiV9ktjGM5nTq0+YurgTyX+YI2lSSfDjI+qU=
github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc=
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0=
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb h1:i1Ppqkc3WQXikh8bXiwHqAN5Rv3/qDCcRk0/Otx73BY=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=

View File

@ -88,7 +88,7 @@ var (
// InputStream are provided in BuildImageOptions
ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream")
// ErrMustSpecifyNames is the error rreturned when the Names field on
// ErrMustSpecifyNames is the error returned when the Names field on
// ExportImagesOptions is nil or empty
ErrMustSpecifyNames = errors.New("must specify at least one name to export")
)
@ -109,7 +109,7 @@ type ListImagesOptions struct {
// See https://goo.gl/BVzauZ for more details.
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
path := "/images/json?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
@ -129,13 +129,14 @@ type ImageHistory struct {
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Tags,omitempty"`
CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty" toml:"CreatedBy,omitempty"`
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"`
}
// ImageHistory returns the history of the image by its name or ID.
//
// See https://goo.gl/fYtxQa for more details.
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
resp, err := c.do("GET", "/images/"+name+"/history", doOptions{})
resp, err := c.do(http.MethodGet, "/images/"+name+"/history", doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchImage
@ -154,7 +155,7 @@ func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
//
// See https://goo.gl/Vd2Pck for more details.
func (c *Client) RemoveImage(name string) error {
resp, err := c.do("DELETE", "/images/"+name, doOptions{})
resp, err := c.do(http.MethodDelete, "/images/"+name, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return ErrNoSuchImage
@ -181,7 +182,7 @@ type RemoveImageOptions struct {
// See https://goo.gl/Vd2Pck 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{context: opts.Context})
resp, err := c.do(http.MethodDelete, uri, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return ErrNoSuchImage
@ -196,7 +197,7 @@ func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error
//
// See https://goo.gl/ncLTG8 for more details.
func (c *Client) InspectImage(name string) (*Image, error) {
resp, err := c.do("GET", "/images/"+name+"/json", doOptions{})
resp, err := c.do(http.MethodGet, "/images/"+name+"/json", doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchImage
@ -271,7 +272,7 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error
name := opts.Name
opts.Name = ""
path := "/images/" + name + "/push?" + queryString(&opts)
return c.stream("POST", path, streamOptions{
return c.stream(http.MethodPost, path, streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
@ -288,6 +289,7 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error
type PullImageOptions struct {
Repository string `qs:"fromImage"`
Tag string
Platform string `ver:"1.32"`
// Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21
// and Docker Engine < 1.9
@ -318,12 +320,16 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error
opts.Repository = parts[0]
opts.Tag = parts[1]
}
return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
return c.createImage(&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, context context.Context) error {
path := "/images/create?" + qs
return c.stream("POST", path, streamOptions{
//nolint:golint
func (c *Client) createImage(opts interface{}, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error {
url, err := c.getPath("/images/create", opts)
if err != nil {
return err
}
return c.streamURL(http.MethodPost, url, streamOptions{
setRawTerminal: true,
headers: headers,
in: in,
@ -347,7 +353,7 @@ type LoadImageOptions struct {
//
// See https://goo.gl/rEsBV3 for more details.
func (c *Client) LoadImage(opts LoadImageOptions) error {
return c.stream("POST", "/images/load", streamOptions{
return c.stream(http.MethodPost, "/images/load", streamOptions{
setRawTerminal: true,
in: opts.InputStream,
stdout: opts.OutputStream,
@ -369,7 +375,7 @@ type ExportImageOptions struct {
//
// See https://goo.gl/AuySaA for more details.
func (c *Client) ExportImage(opts ExportImageOptions) error {
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
return c.stream(http.MethodGet, fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
@ -394,7 +400,28 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
if opts.Names == nil || len(opts.Names) == 0 {
return ErrMustSpecifyNames
}
return c.stream("GET", "/images/get?"+queryString(&opts), streamOptions{
// API < 1.25 allows multiple name values
// 1.25 says name must be a comma separated list
var err error
var exporturl string
if c.requestedAPIVersion.GreaterThanOrEqualTo(apiVersion125) {
str := opts.Names[0]
for _, val := range opts.Names[1:] {
str += "," + val
}
exporturl, err = c.getPath("/images/get", ExportImagesOptions{
Names: []string{str},
OutputStream: opts.OutputStream,
InactivityTimeout: opts.InactivityTimeout,
Context: opts.Context,
})
} else {
exporturl, err = c.getPath("/images/get", &opts)
}
if err != nil {
return err
}
return c.streamURL(http.MethodGet, exporturl, streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
@ -435,7 +462,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, opts.Context)
return c.createImage(&opts, nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
}
// BuildImageOptions present the set of informations available for building an
@ -444,36 +471,40 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
// For more details about the Docker building process, see
// https://goo.gl/4nYHwV.
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"`
ForceRmTmpContainer bool `qs:"forcerm"`
RawJSONStream bool `qs:"-"`
Memory int64 `qs:"memory"`
Memswap int64 `qs:"memswap"`
CPUShares int64 `qs:"cpushares"`
CPUQuota int64 `qs:"cpuquota"`
CPUPeriod int64 `qs:"cpuperiod"`
CPUSetCPUs string `qs:"cpusetcpus"`
Labels map[string]string `qs:"labels"`
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
Remote string `qs:"remote"`
Context context.Context
Name string `qs:"t"`
Dockerfile string `ver:"1.25"`
ExtraHosts string `ver:"1.28"`
CacheFrom []string `qs:"-" ver:"1.25"`
Memory int64
Memswap int64
ShmSize int64
CPUShares int64
CPUQuota int64 `ver:"1.21"`
CPUPeriod int64 `ver:"1.21"`
CPUSetCPUs string
Labels map[string]string
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
Remote string
Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header
AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
ContextDir string `qs:"-"`
Ulimits []ULimit `qs:"-"`
BuildArgs []BuildArg `qs:"-"`
NetworkMode string `qs:"networkmode"`
Ulimits []ULimit `qs:"-" ver:"1.18"`
BuildArgs []BuildArg `qs:"-" ver:"1.21"`
NetworkMode string `ver:"1.25"`
Platform string `ver:"1.32"`
InactivityTimeout time.Duration `qs:"-"`
CgroupParent string `qs:"cgroupparent"`
SecurityOpt []string `qs:"securityopt"`
Target string `gs:"target"`
Context context.Context
CgroupParent string
SecurityOpt []string
Target string
Outputs string `ver:"1.40"`
NoCache bool
SuppressOutput bool `qs:"q"`
Pull bool `ver:"1.16"`
RmTmpContainer bool `qs:"rm"`
ForceRmTmpContainer bool `qs:"forcerm" ver:"1.12"`
RawJSONStream bool `qs:"-"`
}
// BuildArg represents arguments that can be passed to the image when building
@ -516,13 +547,16 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
return err
}
}
qs := queryString(&opts)
qs, ver := queryStringVersion(&opts)
if c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion125) && len(opts.CacheFrom) > 0 {
if 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 ver == nil || apiVersion125.GreaterThan(ver) {
ver = apiVersion125
}
}
}
@ -531,6 +565,9 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
item := url.Values(map[string][]string{})
item.Add("ulimits", string(b))
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
if ver == nil || apiVersion118.GreaterThan(ver) {
ver = apiVersion118
}
}
}
@ -543,10 +580,18 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
item := url.Values(map[string][]string{})
item.Add("buildargs", string(b))
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
if ver == nil || apiVersion121.GreaterThan(ver) {
ver = apiVersion121
}
}
}
return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
buildURL, err := c.pathVersionCheck("/build", qs, ver)
if err != nil {
return err
}
return c.streamURL(http.MethodPost, buildURL, streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
@ -584,10 +629,9 @@ func (c *Client) TagImage(name string, opts TagImageOptions) error {
if name == "" {
return ErrNoSuchImage
}
resp, err := c.do("POST", "/images/"+name+"/tag?"+queryString(&opts), doOptions{
resp, err := c.do(http.MethodPost, "/images/"+name+"/tag?"+queryString(&opts), doOptions{
context: opts.Context,
})
if err != nil {
return err
}
@ -610,7 +654,7 @@ func isURL(u string) bool {
}
func headersWithAuth(auths ...registryAuth) (map[string]string, error) {
var headers = make(map[string]string)
headers := make(map[string]string)
for _, auth := range auths {
if auth.isEmpty() {
@ -641,7 +685,7 @@ type APIImageSearch struct {
//
// See https://goo.gl/KLO9IZ for more details.
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
resp, err := c.do("GET", "/images/search?term="+term, doOptions{})
resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{})
if err != nil {
return nil, err
}
@ -662,7 +706,7 @@ func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImage
return nil, err
}
resp, err := c.do("GET", "/images/search?term="+term, doOptions{
resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{
headers: headers,
})
if err != nil {
@ -700,7 +744,7 @@ type PruneImagesResults struct {
// 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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -1,505 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package archive
import (
"archive/tar"
"bufio"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/pools"
"github.com/docker/docker/pkg/system"
"github.com/sirupsen/logrus"
)
const (
// Uncompressed represents the uncompressed.
Uncompressed Compression = iota
// Bzip2 is bzip2 compression algorithm.
Bzip2
// Gzip is gzip compression algorithm.
Gzip
// Xz is xz compression algorithm.
Xz
)
const (
modeISDIR = 040000 // Directory
modeISFIFO = 010000 // FIFO
modeISREG = 0100000 // Regular file
modeISLNK = 0120000 // Symbolic link
modeISBLK = 060000 // Block special file
modeISCHR = 020000 // Character special file
modeISSOCK = 0140000 // Socket
)
// Compression is the state represents if compressed or not.
type Compression int
// Extension returns the extension of a file that uses the specified compression algorithm.
func (compression *Compression) Extension() string {
switch *compression {
case Uncompressed:
return "tar"
case Bzip2:
return "tar.bz2"
case Gzip:
return "tar.gz"
case Xz:
return "tar.xz"
}
return ""
}
// WhiteoutFormat is the format of whiteouts unpacked
type WhiteoutFormat int
// TarOptions wraps the tar options.
type TarOptions struct {
IncludeFiles []string
ExcludePatterns []string
Compression Compression
NoLchown bool
UIDMaps []idtools.IDMap
GIDMaps []idtools.IDMap
ChownOpts *idtools.Identity
IncludeSourceDir bool
// WhiteoutFormat is the expected on disk format for whiteout files.
// This format will be converted to the standard format on pack
// and from the standard format on unpack.
WhiteoutFormat WhiteoutFormat
// When unpacking, specifies whether overwriting a directory with a
// non-directory is allowed and vice versa.
NoOverwriteDirNonDir bool
// For each include when creating an archive, the included name will be
// replaced with the matching name from this map.
RebaseNames map[string]string
InUserNS bool
}
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
// Fix the source path to work with long path names. This is a no-op
// on platforms other than Windows.
srcPath = fixVolumePathPrefix(srcPath)
pm, err := fileutils.NewPatternMatcher(options.ExcludePatterns)
if err != nil {
return nil, err
}
pipeReader, pipeWriter := io.Pipe()
compressWriter, err := CompressStream(pipeWriter, options.Compression)
if err != nil {
return nil, err
}
go func() {
ta := newTarAppender(
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
compressWriter,
options.ChownOpts,
)
ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat)
defer func() {
// Make sure to check the error on Close.
if err := ta.TarWriter.Close(); err != nil {
logrus.Errorf("Can't close tar writer: %s", err)
}
if err := compressWriter.Close(); err != nil {
logrus.Errorf("Can't close compress writer: %s", err)
}
if err := pipeWriter.Close(); err != nil {
logrus.Errorf("Can't close pipe writer: %s", err)
}
}()
// this buffer is needed for the duration of this piped stream
defer pools.BufioWriter32KPool.Put(ta.Buffer)
// In general we log errors here but ignore them because
// during e.g. a diff operation the container can continue
// mutating the filesystem and we can see transient errors
// from this
stat, err := os.Lstat(srcPath)
if err != nil {
return
}
if !stat.IsDir() {
// We can't later join a non-dir with any includes because the
// 'walk' will error if "file/." is stat-ed and "file" is not a
// directory. So, we must split the source path and use the
// basename as the include.
if len(options.IncludeFiles) > 0 {
logrus.Warn("Tar: Can't archive a file with includes")
}
dir, base := SplitPathDirEntry(srcPath)
srcPath = dir
options.IncludeFiles = []string{base}
}
if len(options.IncludeFiles) == 0 {
options.IncludeFiles = []string{"."}
}
seen := make(map[string]bool)
for _, include := range options.IncludeFiles {
rebaseName := options.RebaseNames[include]
walkRoot := getWalkRoot(srcPath, include)
filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
if err != nil {
logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err)
return nil
}
relFilePath, err := filepath.Rel(srcPath, filePath)
if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
// Error getting relative path OR we are looking
// at the source directory path. Skip in both situations.
return nil
}
if options.IncludeSourceDir && include == "." && relFilePath != "." {
relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
}
skip := false
// If "include" is an exact match for the current file
// then even if there's an "excludePatterns" pattern that
// matches it, don't skip it. IOW, assume an explicit 'include'
// is asking for that file no matter what - which is true
// for some files, like .dockerignore and Dockerfile (sometimes)
if include != relFilePath {
skip, err = pm.Matches(relFilePath)
if err != nil {
logrus.Errorf("Error matching %s: %v", relFilePath, err)
return err
}
}
if skip {
// If we want to skip this file and its a directory
// then we should first check to see if there's an
// excludes pattern (e.g. !dir/file) that starts with this
// dir. If so then we can't skip this dir.
// Its not a dir then so we can just return/skip.
if !f.IsDir() {
return nil
}
// No exceptions (!...) in patterns so just skip dir
if !pm.Exclusions() {
return filepath.SkipDir
}
dirSlash := relFilePath + string(filepath.Separator)
for _, pat := range pm.Patterns() {
if !pat.Exclusion() {
continue
}
if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) {
// found a match - so can't skip this dir
return nil
}
}
// No matching exclusion dir so just skip dir
return filepath.SkipDir
}
if seen[relFilePath] {
return nil
}
seen[relFilePath] = true
// Rename the base resource.
if rebaseName != "" {
var replacement string
if rebaseName != string(filepath.Separator) {
// Special case the root directory to replace with an
// empty string instead so that we don't end up with
// double slashes in the paths.
replacement = rebaseName
}
relFilePath = strings.Replace(relFilePath, include, replacement, 1)
}
if err := ta.addTarFile(filePath, relFilePath); err != nil {
logrus.Errorf("Can't add file %s to tar: %s", filePath, err)
// if pipe is broken, stop writing tar stream to it
if err == io.ErrClosedPipe {
return err
}
}
return nil
})
}
}()
return pipeReader, nil
}
// CompressStream compresses the dest with specified compression algorithm.
func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
p := pools.BufioWriter32KPool
buf := p.Get(dest)
switch compression {
case Uncompressed:
writeBufWrapper := p.NewWriteCloserWrapper(buf, buf)
return writeBufWrapper, nil
case Gzip:
gzWriter := gzip.NewWriter(dest)
writeBufWrapper := p.NewWriteCloserWrapper(buf, gzWriter)
return writeBufWrapper, nil
case Bzip2, Xz:
// archive/bzip2 does not support writing, and there is no xz support at all
// However, this is not a problem as docker only currently generates gzipped tars
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
default:
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
}
}
type tarWhiteoutConverter interface {
ConvertWrite(*tar.Header, string, os.FileInfo) (*tar.Header, error)
ConvertRead(*tar.Header, string) (bool, error)
}
type tarAppender struct {
TarWriter *tar.Writer
Buffer *bufio.Writer
// for hardlink mapping
SeenFiles map[uint64]string
IdentityMapping *idtools.IdentityMapping
ChownOpts *idtools.Identity
// For packing and unpacking whiteout files in the
// non standard format. The whiteout files defined
// by the AUFS standard are used as the tar whiteout
// standard.
WhiteoutConverter tarWhiteoutConverter
}
func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender {
return &tarAppender{
SeenFiles: make(map[uint64]string),
TarWriter: tar.NewWriter(writer),
Buffer: pools.BufioWriter32KPool.Get(nil),
IdentityMapping: idMapping,
ChownOpts: chownOpts,
}
}
// addTarFile adds to the tar archive a file from `path` as `name`
func (ta *tarAppender) addTarFile(path, name string) error {
fi, err := os.Lstat(path)
if err != nil {
return err
}
var link string
if fi.Mode()&os.ModeSymlink != 0 {
var err error
link, err = os.Readlink(path)
if err != nil {
return err
}
}
hdr, err := FileInfoHeader(name, fi, link)
if err != nil {
return err
}
if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil {
return err
}
// if it's not a directory and has more than 1 link,
// it's hard linked, so set the type flag accordingly
if !fi.IsDir() && hasHardlinks(fi) {
inode, err := getInodeFromStat(fi.Sys())
if err != nil {
return err
}
// a link should have a name that it links too
// and that linked name should be first in the tar archive
if oldpath, ok := ta.SeenFiles[inode]; ok {
hdr.Typeflag = tar.TypeLink
hdr.Linkname = oldpath
hdr.Size = 0 // This Must be here for the writer math to add up!
} else {
ta.SeenFiles[inode] = name
}
}
//check whether the file is overlayfs whiteout
//if yes, skip re-mapping container ID mappings.
isOverlayWhiteout := fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0
//handle re-mapping container ID mappings back to host ID mappings before
//writing tar headers/files. We skip whiteout files because they were written
//by the kernel and already have proper ownership relative to the host
if !isOverlayWhiteout &&
!strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) &&
!ta.IdentityMapping.Empty() {
fileIdentity, err := getFileIdentity(fi.Sys())
if err != nil {
return err
}
hdr.Uid, hdr.Gid, err = ta.IdentityMapping.ToContainer(fileIdentity)
if err != nil {
return err
}
}
// explicitly override with ChownOpts
if ta.ChownOpts != nil {
hdr.Uid = ta.ChownOpts.UID
hdr.Gid = ta.ChownOpts.GID
}
if ta.WhiteoutConverter != nil {
wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, path, fi)
if err != nil {
return err
}
// If a new whiteout file exists, write original hdr, then
// replace hdr with wo to be written after. Whiteouts should
// always be written after the original. Note the original
// hdr may have been updated to be a whiteout with returning
// a whiteout header
if wo != nil {
if err := ta.TarWriter.WriteHeader(hdr); err != nil {
return err
}
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
return fmt.Errorf("tar: cannot use whiteout for non-empty file")
}
hdr = wo
}
}
if err := ta.TarWriter.WriteHeader(hdr); err != nil {
return err
}
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
// We use system.OpenSequential to ensure we use sequential file
// access on Windows to avoid depleting the standby list.
// On Linux, this equates to a regular os.Open.
file, err := system.OpenSequential(path)
if err != nil {
return err
}
ta.Buffer.Reset(ta.TarWriter)
defer ta.Buffer.Reset(nil)
_, err = io.Copy(ta.Buffer, file)
file.Close()
if err != nil {
return err
}
err = ta.Buffer.Flush()
if err != nil {
return err
}
}
return nil
}
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
// to a tar header
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
capability, _ := system.Lgetxattr(path, "security.capability")
if capability != nil {
hdr.Xattrs = make(map[string]string)
hdr.Xattrs["security.capability"] = string(capability)
}
return nil
}
// FileInfoHeader creates a populated Header from fi.
// Compared to archive pkg this function fills in more information.
// Also, regardless of Go version, this function fills file type bits (e.g. hdr.Mode |= modeISDIR),
// which have been deleted since Go 1.9 archive/tar.
func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
hdr, err := tar.FileInfoHeader(fi, link)
if err != nil {
return nil, err
}
hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi)
name, err = canonicalTarName(name, fi.IsDir())
if err != nil {
return nil, fmt.Errorf("tar: cannot canonicalize path: %v", err)
}
hdr.Name = name
if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil {
return nil, err
}
return hdr, nil
}
// fillGo18FileTypeBits fills type bits which have been removed on Go 1.9 archive/tar
// https://github.com/golang/go/commit/66b5a2f
func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
fm := fi.Mode()
switch {
case fm.IsRegular():
mode |= modeISREG
case fi.IsDir():
mode |= modeISDIR
case fm&os.ModeSymlink != 0:
mode |= modeISLNK
case fm&os.ModeDevice != 0:
if fm&os.ModeCharDevice != 0 {
mode |= modeISCHR
} else {
mode |= modeISBLK
}
case fm&os.ModeNamedPipe != 0:
mode |= modeISFIFO
case fm&os.ModeSocket != 0:
mode |= modeISSOCK
}
return mode
}
// canonicalTarName provides a platform-independent and consistent posix-style
//path for files and directories to be archived regardless of the platform.
func canonicalTarName(name string, isDir bool) (string, error) {
name, err := CanonicalTarNameForPath(name)
if err != nil {
return "", err
}
// suffix with '/' for directories
if isDir && !strings.HasSuffix(name, "/") {
name += "/"
}
return name, nil
}

View File

@ -1,104 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package archive
import (
"archive/tar"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/system"
"golang.org/x/sys/unix"
)
const (
// AUFSWhiteoutFormat is the default format for whiteouts
AUFSWhiteoutFormat WhiteoutFormat = iota
// OverlayWhiteoutFormat formats whiteout according to the overlay
// standard.
OverlayWhiteoutFormat
)
func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
if format == OverlayWhiteoutFormat {
return overlayWhiteoutConverter{}
}
return nil
}
type overlayWhiteoutConverter struct{}
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
// convert whiteouts to AUFS format
if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
// we just rename the file and make it normal
dir, filename := filepath.Split(hdr.Name)
hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename)
hdr.Mode = 0600
hdr.Typeflag = tar.TypeReg
hdr.Size = 0
}
if fi.Mode()&os.ModeDir != 0 {
// convert opaque dirs to AUFS format by writing an empty file with the prefix
opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque")
if err != nil {
return nil, err
}
if len(opaque) == 1 && opaque[0] == 'y' {
if hdr.Xattrs != nil {
delete(hdr.Xattrs, "trusted.overlay.opaque")
}
// create a header for the whiteout file
// it should inherit some properties from the parent, but be a regular file
wo = &tar.Header{
Typeflag: tar.TypeReg,
Mode: hdr.Mode & int64(os.ModePerm),
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir),
Size: 0,
Uid: hdr.Uid,
Uname: hdr.Uname,
Gid: hdr.Gid,
Gname: hdr.Gname,
AccessTime: hdr.AccessTime,
ChangeTime: hdr.ChangeTime,
}
}
}
return
}
func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
base := filepath.Base(path)
dir := filepath.Dir(path)
// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
if base == WhiteoutOpaqueDir {
err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
// don't write the file itself
return false, err
}
// if a file was deleted and we are using overlay, we need to create a character device
if strings.HasPrefix(base, WhiteoutPrefix) {
originalBase := base[len(WhiteoutPrefix):]
originalPath := filepath.Join(dir, originalBase)
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
return false, err
}
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
return false, err
}
// don't write the file itself
return false, nil
}
return true, nil
}

View File

@ -1,11 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
// +build !linux
package archive
func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
return nil
}

View File

@ -1,77 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
// +build !windows
package archive
import (
"archive/tar"
"errors"
"os"
"path/filepath"
"syscall"
"github.com/docker/docker/pkg/idtools"
"golang.org/x/sys/unix"
)
// CanonicalTarNameForPath returns platform-specific filepath
// to canonical posix-style path for tar archival. p is relative
// path.
func CanonicalTarNameForPath(p string) (string, error) {
return p, nil // already unix-style
}
// fixVolumePathPrefix does platform specific processing to ensure that if
// the path being passed in is not in a volume path format, convert it to one.
func fixVolumePathPrefix(srcPath string) string {
return srcPath
}
// getWalkRoot calculates the root path when performing a TarWithOptions.
// We use a separate function as this is platform specific. On Linux, we
// can't use filepath.Join(srcPath,include) because this will clean away
// a trailing "." or "/" which may be important.
func getWalkRoot(srcPath string, include string) string {
return srcPath + string(filepath.Separator) + include
}
func getInodeFromStat(stat interface{}) (inode uint64, err error) {
s, ok := stat.(*syscall.Stat_t)
if ok {
inode = uint64(s.Ino)
}
return
}
func getFileIdentity(stat interface{}) (idtools.Identity, error) {
s, ok := stat.(*syscall.Stat_t)
if !ok {
return idtools.Identity{}, errors.New("cannot convert stat value to syscall.Stat_t")
}
return idtools.Identity{UID: int(s.Uid), GID: int(s.Gid)}, nil
}
func chmodTarEntry(perm os.FileMode) os.FileMode {
return perm // noop for unix as golang APIs provide perm bits correctly
}
func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {
s, ok := stat.(*syscall.Stat_t)
if ok {
// Currently go does not fill in the major/minors
if s.Mode&unix.S_IFBLK != 0 ||
s.Mode&unix.S_IFCHR != 0 {
hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) // nolint: unconvert
hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) // nolint: unconvert
}
}
return
}

View File

@ -1,71 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package archive
import (
"archive/tar"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/longpath"
)
// CanonicalTarNameForPath returns platform-specific filepath
// to canonical posix-style path for tar archival. p is relative
// path.
func CanonicalTarNameForPath(p string) (string, error) {
// windows: convert windows style relative path with backslashes
// into forward slashes. Since windows does not allow '/' or '\'
// in file names, it is mostly safe to replace however we must
// check just in case
if strings.Contains(p, "/") {
return "", fmt.Errorf("Windows path contains forward slash: %s", p)
}
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil
}
// fixVolumePathPrefix does platform specific processing to ensure that if
// the path being passed in is not in a volume path format, convert it to one.
func fixVolumePathPrefix(srcPath string) string {
return longpath.AddPrefix(srcPath)
}
// getWalkRoot calculates the root path when performing a TarWithOptions.
// We use a separate function as this is platform specific.
func getWalkRoot(srcPath string, include string) string {
return filepath.Join(srcPath, include)
}
func getInodeFromStat(stat interface{}) (inode uint64, err error) {
// do nothing. no notion of Inode in stat on Windows
return
}
func getFileIdentity(stat interface{}) (idtools.Identity, error) {
// no notion of file ownership mapping yet on Windows
return idtools.Identity{}, nil
}
// chmodTarEntry is used to adjust the file permissions used in tar header based
// on the platform the archival is done.
func chmodTarEntry(perm os.FileMode) os.FileMode {
//perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
permPart := perm & os.ModePerm
noPermPart := perm &^ os.ModePerm
// Add the x bit: make everything +x from windows
permPart |= 0111
permPart &= 0755
return noPermPart | permPart
}
func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {
// do nothing. no notion of Rdev, Nlink in stat on Windows
return
}

View File

@ -1,16 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
// +build !windows
package archive
import (
"os"
"syscall"
)
func hasHardlinks(fi os.FileInfo) bool {
return fi.Sys().(*syscall.Stat_t).Nlink > 1
}

View File

@ -1,11 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package archive
import "os"
func hasHardlinks(fi os.FileInfo) bool {
return false
}

View File

@ -1,29 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package archive
import (
"os"
"path/filepath"
)
// SplitPathDirEntry splits the given path between its directory name and its
// basename by first cleaning the path but preserves a trailing "." if the
// original path specified the current directory.
func SplitPathDirEntry(path string) (dir, base string) {
cleanedPath := filepath.Clean(filepath.FromSlash(path))
if specifiesCurrentDir(path) {
cleanedPath += string(os.PathSeparator) + "."
}
return filepath.Dir(cleanedPath), filepath.Base(cleanedPath)
}
// specifiesCurrentDir returns whether the given path specifies
// a "current directory", i.e., the last path segment is `.`.
func specifiesCurrentDir(path string) bool {
return filepath.Base(path) == "."
}

View File

@ -1,27 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package archive
// Whiteouts are files with a special meaning for the layered filesystem.
// Docker uses AUFS whiteout files inside exported archives. In other
// filesystems these files are generated/handled on tar creation/extraction.
// WhiteoutPrefix prefix means file is a whiteout. If this is followed by a
// filename this means that file has been removed from the base layer.
const WhiteoutPrefix = ".wh."
// WhiteoutMetaPrefix prefix means whiteout has a special meaning and is not
// for removing an actual file. Normally these files are excluded from exported
// archives.
const WhiteoutMetaPrefix = WhiteoutPrefix + WhiteoutPrefix
// WhiteoutLinkDir is a directory AUFS uses for storing hardlink links to other
// layers. Normally these should not go into exported archives and all changed
// hardlinks should be copied to the top layer.
const WhiteoutLinkDir = WhiteoutMetaPrefix + "plnk"
// WhiteoutOpaqueDir file means directory has been made opaque - meaning
// readdir calls to this directory do not follow to lower layers.
const WhiteoutOpaqueDir = WhiteoutMetaPrefix + ".opq"

View File

@ -1,402 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package jsonmessage
import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/Nvveen/Gotty"
"github.com/docker/go-units"
"github.com/fsouza/go-dockerclient/internal/term"
)
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
// ensure the formatted time isalways the same number of characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
// JSONError wraps a concrete Code and Message, `Code` is
// is an integer error code, `Message` is the error message.
type JSONError struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
func (e *JSONError) Error() string {
return e.Message
}
// JSONProgress describes a Progress. terminalFd is the fd of the current terminal,
// Start is the initial value for the operation. Current is the current status and
// value of the progress made towards Total. Total is the end value describing when
// we made 100% progress for an operation.
type JSONProgress struct {
terminalFd uintptr
Current int64 `json:"current,omitempty"`
Total int64 `json:"total,omitempty"`
Start int64 `json:"start,omitempty"`
// If true, don't show xB/yB
HideCounts bool `json:"hidecounts,omitempty"`
Units string `json:"units,omitempty"`
nowFunc func() time.Time
winSize int
}
func (p *JSONProgress) String() string {
var (
width = p.width()
pbBox string
numbersBox string
timeLeftBox string
)
if p.Current <= 0 && p.Total <= 0 {
return ""
}
if p.Total <= 0 {
switch p.Units {
case "":
current := units.HumanSize(float64(p.Current))
return fmt.Sprintf("%8v", current)
default:
return fmt.Sprintf("%d %s", p.Current, p.Units)
}
}
percentage := int(float64(p.Current)/float64(p.Total)*100) / 2
if percentage > 50 {
percentage = 50
}
if width > 110 {
// this number can't be negative gh#7136
numSpaces := 0
if 50-percentage > 0 {
numSpaces = 50 - percentage
}
pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
}
switch {
case p.HideCounts:
case p.Units == "": // no units, use bytes
current := units.HumanSize(float64(p.Current))
total := units.HumanSize(float64(p.Total))
numbersBox = fmt.Sprintf("%8v/%v", current, total)
if p.Current > p.Total {
// remove total display if the reported current is wonky.
numbersBox = fmt.Sprintf("%8v", current)
}
default:
numbersBox = fmt.Sprintf("%d/%d %s", p.Current, p.Total, p.Units)
if p.Current > p.Total {
// remove total display if the reported current is wonky.
numbersBox = fmt.Sprintf("%d %s", p.Current, p.Units)
}
}
if p.Current > 0 && p.Start > 0 && percentage < 50 {
fromStart := p.now().Sub(time.Unix(p.Start, 0))
perEntry := fromStart / time.Duration(p.Current)
left := time.Duration(p.Total-p.Current) * perEntry
left = (left / time.Second) * time.Second
if width > 50 {
timeLeftBox = " " + left.String()
}
}
return pbBox + numbersBox + timeLeftBox
}
// shim for testing
func (p *JSONProgress) now() time.Time {
if p.nowFunc == nil {
p.nowFunc = func() time.Time {
return time.Now().UTC()
}
}
return p.nowFunc()
}
// shim for testing
func (p *JSONProgress) width() int {
if p.winSize != 0 {
return p.winSize
}
ws, err := term.GetWinsize(p.terminalFd)
if err == nil {
return int(ws.Width)
}
return 200
}
// JSONMessage defines a message struct. It describes
// the created time, where it from, status, ID of the
// message. It's used for docker events.
type JSONMessage struct {
Stream string `json:"stream,omitempty"`
Status string `json:"status,omitempty"`
Progress *JSONProgress `json:"progressDetail,omitempty"`
ProgressMessage string `json:"progress,omitempty"` //deprecated
ID string `json:"id,omitempty"`
From string `json:"from,omitempty"`
Time int64 `json:"time,omitempty"`
TimeNano int64 `json:"timeNano,omitempty"`
Error *JSONError `json:"errorDetail,omitempty"`
ErrorMessage string `json:"error,omitempty"` //deprecated
// Aux contains out-of-band data, such as digests for push signing and image id after building.
Aux *json.RawMessage `json:"aux,omitempty"`
}
/* Satisfied by gotty.TermInfo as well as noTermInfo from below */
type termInfo interface {
Parse(attr string, params ...interface{}) (string, error)
}
type noTermInfo struct{} // canary used when no terminfo.
func (ti *noTermInfo) Parse(attr string, params ...interface{}) (string, error) {
return "", fmt.Errorf("noTermInfo")
}
func clearLine(out io.Writer, ti termInfo) error {
// el2 (clear whole line) is not exposed by terminfo.
// First clear line from beginning to cursor
if attr, err := ti.Parse("el1"); err == nil {
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(out, "\x1b[1K")
if err != nil {
return err
}
}
// Then clear line from cursor to end
if attr, err := ti.Parse("el"); err == nil {
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(out, "\x1b[K")
if err != nil {
return err
}
}
return nil
}
func cursorUp(out io.Writer, ti termInfo, l int) error {
if l == 0 { // Should never be the case, but be tolerant
return nil
}
if attr, err := ti.Parse("cuu", l); err == nil {
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(out, "\x1b[%dA", l)
if err != nil {
return err
}
}
return nil
}
func cursorDown(out io.Writer, ti termInfo, l int) error {
if l == 0 { // Should never be the case, but be tolerant
return nil
}
if attr, err := ti.Parse("cud", l); err == nil {
_, err = fmt.Fprintf(out, "%s", attr)
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(out, "\x1b[%dB", l)
if err != nil {
return err
}
}
return nil
}
// Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out`
// is a terminal. If this is the case, it will erase the entire current line
// when displaying the progressbar.
func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
if jm.Error != nil {
if jm.Error.Code == 401 {
return fmt.Errorf("authentication is required")
}
return jm.Error
}
var endl string
if termInfo != nil && jm.Stream == "" && jm.Progress != nil {
clearLine(out, termInfo)
endl = "\r"
_, err := fmt.Fprintf(out, endl)
if err != nil {
return err
}
} else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
return nil
}
if jm.TimeNano != 0 {
_, err := fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed))
if err != nil {
return err
}
} else if jm.Time != 0 {
_, err := fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed))
if err != nil {
return err
}
}
if jm.ID != "" {
_, err := fmt.Fprintf(out, "%s: ", jm.ID)
if err != nil {
return err
}
}
if jm.From != "" {
_, err := fmt.Fprintf(out, "(from %s) ", jm.From)
if err != nil {
return err
}
}
if jm.Progress != nil && termInfo != nil {
_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
if err != nil {
return err
}
} else if jm.ProgressMessage != "" { //deprecated
_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
if err != nil {
return err
}
} else if jm.Stream != "" {
_, err := fmt.Fprintf(out, "%s%s", jm.Stream, endl)
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
if err != nil {
return err
}
}
return nil
}
// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal`
// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of
// each line and move the cursor while displaying.
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
var (
dec = json.NewDecoder(in)
ids = make(map[string]int)
)
var termInfo termInfo
if isTerminal {
term := os.Getenv("TERM")
if term == "" {
term = "vt102"
}
var err error
if termInfo, err = gotty.OpenTermInfo(term); err != nil {
termInfo = &noTermInfo{}
}
}
for {
diff := 0
var jm JSONMessage
if err := dec.Decode(&jm); err != nil {
if err == io.EOF {
break
}
return err
}
if jm.Aux != nil {
if auxCallback != nil {
auxCallback(jm)
}
continue
}
if jm.Progress != nil {
jm.Progress.terminalFd = terminalFd
}
if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
line, ok := ids[jm.ID]
if !ok {
// NOTE: This approach of using len(id) to
// figure out the number of lines of history
// only works as long as we clear the history
// when we output something that's not
// accounted for in the map, such as a line
// with no ID.
line = len(ids)
ids[jm.ID] = line
if termInfo != nil {
_, err := fmt.Fprintf(out, "\n")
if err != nil {
return err
}
}
}
diff = len(ids) - line
if termInfo != nil {
if err := cursorUp(out, termInfo, diff); err != nil {
return err
}
}
} else {
// When outputting something that isn't progress
// output, clear the history of previous lines. We
// don't want progress entries from some previous
// operation to be updated (for example, pull -a
// with multiple tags).
ids = make(map[string]int)
}
err := jm.Display(out, termInfo)
if jm.ID != "" && termInfo != nil {
if err := cursorDown(out, termInfo, diff); err != nil {
return err
}
}
if err != nil {
return err
}
}
return nil
}
type stream interface {
io.Writer
FD() uintptr
IsTerminal() bool
}
// DisplayJSONMessagesToStream prints json messages to the output stream
func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(JSONMessage)) error {
return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback)
}

View File

@ -1,13 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package term
// Winsize represents the size of the terminal window.
type Winsize struct {
Height uint16
Width uint16
x uint16
y uint16
}

View File

@ -1,16 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
// +build !windows
package term
import "golang.org/x/sys/unix"
// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
return ws, err
}

View File

@ -1,22 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package term
import "github.com/Azure/go-ansiterm/winterm"
// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
info, err := winterm.GetConsoleScreenBufferInfo(fd)
if err != nil {
return nil, err
}
winsize := &Winsize{
Width: uint16(info.Window.Right - info.Window.Left + 1),
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
}
return winsize, nil
}

View File

@ -8,6 +8,7 @@ import (
"context"
"encoding/json"
"net"
"net/http"
"strings"
"github.com/docker/docker/api/types/swarm"
@ -17,12 +18,12 @@ import (
//
// See https://goo.gl/mU7yje for more details.
func (c *Client) Version() (*Env, error) {
return c.VersionWithContext(nil)
return c.VersionWithContext(context.TODO())
}
// VersionWithContext returns version information about the docker server.
func (c *Client) VersionWithContext(ctx context.Context) (*Env, error) {
resp, err := c.do("GET", "/version", doOptions{context: ctx})
resp, err := c.do(http.MethodGet, "/version", doOptions{context: ctx})
if err != nil {
return nil, err
}
@ -37,6 +38,7 @@ func (c *Client) VersionWithContext(ctx context.Context) (*Env, error) {
// DockerInfo contains information about the Docker server
//
// See https://goo.gl/bHUoz9 for more details.
//nolint:golint
type DockerInfo struct {
ID string
Containers int
@ -48,19 +50,6 @@ type DockerInfo struct {
DriverStatus [][2]string
SystemStatus [][2]string
Plugins PluginsInfo
MemoryLimit bool
SwapLimit bool
KernelMemory bool
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool
CPUSet bool
IPv4Forwarding bool
BridgeNfIptables bool
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
Debug bool
OomKillDisable bool
ExperimentalBuild bool
NFd int
NGoroutines int
SystemTime string
@ -90,8 +79,21 @@ type DockerInfo struct {
Isolation string
InitBinary string
DefaultRuntime string
LiveRestoreEnabled bool
Swarm swarm.Info
LiveRestoreEnabled bool
MemoryLimit bool
SwapLimit bool
KernelMemory bool
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool
CPUSet bool
IPv4Forwarding bool
BridgeNfIptables bool
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
Debug bool
OomKillDisable bool
ExperimentalBuild bool
}
// Runtime describes an OCI runtime
@ -162,7 +164,7 @@ type IndexInfo struct {
//
// See https://goo.gl/ElTHi2 for more details.
func (c *Client) Info() (*DockerInfo, error) {
resp, err := c.do("GET", "/info", doOptions{})
resp, err := c.do(http.MethodGet, "/info", doOptions{})
if err != nil {
return nil, err
}

View File

@ -48,7 +48,7 @@ type Endpoint struct {
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) ListNetworks() ([]Network, error) {
resp, err := c.do("GET", "/networks", doOptions{})
resp, err := c.do(http.MethodGet, "/networks", doOptions{})
if err != nil {
return nil, err
}
@ -75,7 +75,7 @@ func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error)
qs := make(url.Values)
qs.Add("filters", string(params))
path := "/networks?" + qs.Encode()
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
return nil, err
}
@ -92,7 +92,7 @@ func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error)
// See https://goo.gl/6GugX3 for more details.
func (c *Client) NetworkInfo(id string) (*Network, error) {
path := "/networks/" + id
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchNetwork{ID: id}
@ -114,15 +114,26 @@ func (c *Client) NetworkInfo(id string) (*Network, error) {
type CreateNetworkOptions struct {
Name string `json:"Name" yaml:"Name" toml:"Name"`
Driver string `json:"Driver" yaml:"Driver" toml:"Driver"`
Scope string `json:"Scope" yaml:"Scope" toml:"Scope"`
IPAM *IPAMOptions `json:"IPAM,omitempty" yaml:"IPAM" toml:"IPAM"`
ConfigFrom *NetworkConfigFrom `json:"ConfigFrom,omitempty" yaml:"ConfigFrom" toml:"ConfigFrom"`
Options map[string]interface{} `json:"Options" yaml:"Options" toml:"Options"`
Labels map[string]string `json:"Labels" yaml:"Labels" toml:"Labels"`
CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate" toml:"CheckDuplicate"`
Internal bool `json:"Internal" yaml:"Internal" toml:"Internal"`
EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6" toml:"EnableIPv6"`
Attachable bool `json:"Attachable" yaml:"Attachable" toml:"Attachable"`
ConfigOnly bool `json:"ConfigOnly" yaml:"ConfigOnly" toml:"ConfigOnly"`
Ingress bool `json:"Ingress" yaml:"Ingress" toml:"Ingress"`
Context context.Context `json:"-"`
}
// NetworkConfigFrom is used in network creation for specifying the source of a
// network configuration.
type NetworkConfigFrom struct {
Network string `json:"Network" yaml:"Network" toml:"Network"`
}
// IPAMOptions controls IP Address Management when creating a network
//
// See https://goo.gl/T8kRVH for more details.
@ -148,7 +159,7 @@ type IPAMConfig struct {
// See https://goo.gl/6GugX3 for more details.
func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
resp, err := c.do(
"POST",
http.MethodPost,
"/networks/create",
doOptions{
data: opts,
@ -182,7 +193,7 @@ func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) RemoveNetwork(id string) error {
resp, err := c.do("DELETE", "/networks/"+id, doOptions{})
resp, err := c.do(http.MethodDelete, "/networks/"+id, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetwork{ID: id}
@ -225,6 +236,7 @@ type EndpointConfig struct {
GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"`
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"`
MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"`
DriverOpts map[string]string `json:"DriverOpts,omitempty" yaml:"DriverOpts,omitempty" toml:"DriverOpts,omitempty"`
}
// EndpointIPAMConfig represents IPAM configurations for an
@ -241,7 +253,7 @@ 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{
resp, err := c.do(http.MethodPost, "/networks/"+id+"/connect", doOptions{
data: opts,
context: opts.Context,
})
@ -260,7 +272,7 @@ func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error {
resp, err := c.do("POST", "/networks/"+id+"/disconnect", doOptions{data: opts})
resp, err := c.do(http.MethodPost, "/networks/"+id+"/disconnect", doOptions{data: opts})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
@ -291,7 +303,7 @@ type PruneNetworksResults struct {
// 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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -35,15 +35,26 @@ type InstallPluginOptions struct {
//
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) InstallPlugins(opts InstallPluginOptions) error {
headers, err := headersWithAuth(opts.Auth)
if err != nil {
return err
}
path := "/plugins/pull?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Plugins,
context: opts.Context,
headers: headers,
})
defer resp.Body.Close()
if err != nil {
return err
}
defer resp.Body.Close()
// PullPlugin streams back the progress of the pull, we must consume the whole body
// otherwise the pull will be canceled on the engine.
if _, err := ioutil.ReadAll(resp.Body); err != nil {
return err
}
return nil
}
@ -152,7 +163,7 @@ type PluginDetail struct {
//
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) ListPlugins(ctx context.Context) ([]PluginDetail, error) {
resp, err := c.do("GET", "/plugins", doOptions{
resp, err := c.do(http.MethodGet, "/plugins", doOptions{
context: ctx,
})
if err != nil {
@ -179,7 +190,7 @@ type ListFilteredPluginsOptions struct {
// See https://goo.gl/rmdmWg for more details.
func (c *Client) ListFilteredPlugins(opts ListFilteredPluginsOptions) ([]PluginDetail, error) {
path := "/plugins/json?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{
resp, err := c.do(http.MethodGet, path, doOptions{
context: opts.Context,
})
if err != nil {
@ -193,12 +204,41 @@ func (c *Client) ListFilteredPlugins(opts ListFilteredPluginsOptions) ([]PluginD
return pluginDetails, nil
}
// GetPluginPrivileges returns pulginPrivileges or an error.
// GetPluginPrivileges returns pluginPrivileges or an error.
//
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) GetPluginPrivileges(name string, ctx context.Context) ([]PluginPrivilege, error) {
resp, err := c.do("GET", "/plugins/privileges?remote="+name, doOptions{
context: ctx,
//nolint:golint
func (c *Client) GetPluginPrivileges(remote string, ctx context.Context) ([]PluginPrivilege, error) {
return c.GetPluginPrivilegesWithOptions(
GetPluginPrivilegesOptions{
Remote: remote,
Context: ctx,
})
}
// GetPluginPrivilegesOptions specify parameters to the GetPluginPrivilegesWithOptions function.
//
// See https://goo.gl/C4t7Tz for more details.
type GetPluginPrivilegesOptions struct {
Remote string
Auth AuthConfiguration
Context context.Context
}
// GetPluginPrivilegesWithOptions returns pluginPrivileges or an error.
//
// See https://goo.gl/C4t7Tz for more details.
//nolint:golint
func (c *Client) GetPluginPrivilegesWithOptions(opts GetPluginPrivilegesOptions) ([]PluginPrivilege, error) {
headers, err := headersWithAuth(opts.Auth)
if err != nil {
return nil, err
}
path := "/plugins/privileges?" + queryString(opts)
resp, err := c.do(http.MethodGet, path, doOptions{
context: opts.Context,
headers: headers,
})
if err != nil {
return nil, err
@ -214,21 +254,18 @@ func (c *Client) GetPluginPrivileges(name string, ctx context.Context) ([]Plugin
// InspectPlugins returns a pluginDetail or an error.
//
// See https://goo.gl/C4t7Tz for more details.
//nolint:golint
func (c *Client) InspectPlugins(name string, ctx context.Context) (*PluginDetail, error) {
resp, err := c.do("GET", "/plugins/"+name+"/json", doOptions{
resp, err := c.do(http.MethodGet, "/plugins/"+name+"/json", doOptions{
context: ctx,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchPlugin{ID: name}
}
return nil, err
}
resp.Body.Close()
defer resp.Body.Close()
var pluginDetail PluginDetail
if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil {
return nil, err
@ -252,20 +289,26 @@ type RemovePluginOptions struct {
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) RemovePlugin(opts RemovePluginOptions) (*PluginDetail, error) {
path := "/plugins/" + opts.Name + "?" + queryString(opts)
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchPlugin{ID: opts.Name}
}
return nil, err
}
resp.Body.Close()
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if len(body) == 0 {
// Seems like newer docker versions won't return the plugindetail after removal
return nil, nil
}
var pluginDetail PluginDetail
if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil {
if err := json.Unmarshal(body, &pluginDetail); err != nil {
return nil, err
}
return &pluginDetail, nil
@ -287,8 +330,7 @@ type EnablePluginOptions struct {
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) EnablePlugin(opts EnablePluginOptions) error {
path := "/plugins/" + opts.Name + "/enable?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
defer resp.Body.Close()
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return err
}
@ -311,8 +353,7 @@ type DisablePluginOptions struct {
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) DisablePlugin(opts DisablePluginOptions) error {
path := "/plugins/" + opts.Name + "/disable"
resp, err := c.do("POST", path, doOptions{context: opts.Context})
defer resp.Body.Close()
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return err
}
@ -337,13 +378,14 @@ type CreatePluginOptions struct {
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) CreatePlugin(opts CreatePluginOptions) (string, error) {
path := "/plugins/create?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Path,
context: opts.Context})
defer resp.Body.Close()
context: opts.Context,
})
if err != nil {
return "", err
}
defer resp.Body.Close()
containerNameBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
@ -366,11 +408,11 @@ type PushPluginOptions struct {
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) PushPlugin(opts PushPluginOptions) error {
path := "/plugins/" + opts.Name + "/push"
resp, err := c.do("POST", path, doOptions{context: opts.Context})
defer resp.Body.Close()
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return err
}
resp.Body.Close()
return nil
}
@ -390,17 +432,17 @@ type ConfigurePluginOptions struct {
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) ConfigurePlugin(opts ConfigurePluginOptions) error {
path := "/plugins/" + opts.Name + "/set"
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Envs,
context: opts.Context,
})
defer resp.Body.Close()
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchPlugin{ID: opts.Name}
}
return err
}
resp.Body.Close()
return nil
}

View File

@ -36,7 +36,7 @@ type InitSwarmOptions struct {
// 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{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.InitRequest,
forceJSON: true,
context: opts.Context,
@ -66,7 +66,7 @@ type JoinSwarmOptions struct {
// 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{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.JoinRequest,
forceJSON: true,
context: opts.Context,
@ -93,7 +93,7 @@ func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error {
params := make(url.Values)
params.Set("force", strconv.FormatBool(opts.Force))
path := "/swarm/leave?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
context: opts.Context,
})
if err != nil {
@ -123,7 +123,7 @@ func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error {
params.Set("rotateWorkerToken", strconv.FormatBool(opts.RotateWorkerToken))
params.Set("rotateManagerToken", strconv.FormatBool(opts.RotateManagerToken))
path := "/swarm/update?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Swarm,
forceJSON: true,
context: opts.Context,
@ -141,7 +141,7 @@ func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error {
// 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{
resp, err := c.do(http.MethodGet, "/swarm", doOptions{
context: ctx,
})
if err != nil {

View File

@ -46,7 +46,7 @@ func (c *Client) CreateConfig(opts CreateConfigOptions) (*swarm.Config, error) {
return nil, err
}
path := "/configs/create?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
headers: headers,
data: opts.ConfigSpec,
forceJSON: true,
@ -76,7 +76,7 @@ type RemoveConfigOptions struct {
// See https://goo.gl/Tqrtya for more details.
func (c *Client) RemoveConfig(opts RemoveConfigOptions) error {
path := "/configs/" + opts.ID
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchConfig{ID: opts.ID}
@ -109,7 +109,7 @@ func (c *Client) UpdateConfig(id string, opts UpdateConfigOptions) error {
}
params := make(url.Values)
params.Set("version", strconv.FormatUint(opts.Version, 10))
resp, err := c.do("POST", "/configs/"+id+"/update?"+params.Encode(), doOptions{
resp, err := c.do(http.MethodPost, "/configs/"+id+"/update?"+params.Encode(), doOptions{
headers: headers,
data: opts.ConfigSpec,
forceJSON: true,
@ -130,7 +130,7 @@ func (c *Client) UpdateConfig(id string, opts UpdateConfigOptions) error {
// See https://goo.gl/dHmr75 for more details.
func (c *Client) InspectConfig(id string) (*swarm.Config, error) {
path := "/configs/" + id
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchConfig{ID: id}
@ -158,7 +158,7 @@ type ListConfigsOptions struct {
// See https://goo.gl/DwvNMd for more details.
func (c *Client) ListConfigs(opts ListConfigsOptions) ([]swarm.Config, error) {
path := "/configs?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -40,7 +40,7 @@ type ListNodesOptions struct {
// See http://goo.gl/3K4GwU for more details.
func (c *Client) ListNodes(opts ListNodesOptions) ([]swarm.Node, error) {
path := "/nodes?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
@ -56,7 +56,7 @@ func (c *Client) ListNodes(opts ListNodesOptions) ([]swarm.Node, error) {
//
// See http://goo.gl/WjkTOk for more details.
func (c *Client) InspectNode(id string) (*swarm.Node, error) {
resp, err := c.do("GET", "/nodes/"+id, doOptions{})
resp, err := c.do(http.MethodGet, "/nodes/"+id, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchNode{ID: id}
@ -87,7 +87,7 @@ func (c *Client) UpdateNode(id string, opts UpdateNodeOptions) error {
params := make(url.Values)
params.Set("version", strconv.FormatUint(opts.Version, 10))
path := "/nodes/" + id + "/update?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
context: opts.Context,
forceJSON: true,
data: opts.NodeSpec,
@ -118,7 +118,7 @@ func (c *Client) RemoveNode(opts RemoveNodeOptions) error {
params := make(url.Values)
params.Set("force", strconv.FormatBool(opts.Force))
path := "/nodes/" + opts.ID + "?" + params.Encode()
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNode{ID: opts.ID}

View File

@ -46,7 +46,7 @@ func (c *Client) CreateSecret(opts CreateSecretOptions) (*swarm.Secret, error) {
return nil, err
}
path := "/secrets/create?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
headers: headers,
data: opts.SecretSpec,
forceJSON: true,
@ -76,7 +76,7 @@ type RemoveSecretOptions struct {
// See https://goo.gl/Tqrtya for more details.
func (c *Client) RemoveSecret(opts RemoveSecretOptions) error {
path := "/secrets/" + opts.ID
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchSecret{ID: opts.ID}
@ -109,7 +109,7 @@ func (c *Client) UpdateSecret(id string, opts UpdateSecretOptions) error {
}
params := make(url.Values)
params.Set("version", strconv.FormatUint(opts.Version, 10))
resp, err := c.do("POST", "/secrets/"+id+"/update?"+params.Encode(), doOptions{
resp, err := c.do(http.MethodPost, "/secrets/"+id+"/update?"+params.Encode(), doOptions{
headers: headers,
data: opts.SecretSpec,
forceJSON: true,
@ -130,7 +130,7 @@ func (c *Client) UpdateSecret(id string, opts UpdateSecretOptions) error {
// See https://goo.gl/dHmr75 for more details.
func (c *Client) InspectSecret(id string) (*swarm.Secret, error) {
path := "/secrets/" + id
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchSecret{ID: id}
@ -158,7 +158,7 @@ type ListSecretsOptions struct {
// See https://goo.gl/DwvNMd for more details.
func (c *Client) ListSecrets(opts ListSecretsOptions) ([]swarm.Secret, error) {
path := "/secrets?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -46,7 +46,7 @@ func (c *Client) CreateService(opts CreateServiceOptions) (*swarm.Service, error
return nil, err
}
path := "/services/create?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
headers: headers,
data: opts.ServiceSpec,
forceJSON: true,
@ -76,7 +76,7 @@ type RemoveServiceOptions struct {
// See https://goo.gl/Tqrtya for more details.
func (c *Client) RemoveService(opts RemoveServiceOptions) error {
path := "/services/" + opts.ID
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchService{ID: opts.ID}
@ -106,7 +106,7 @@ func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error {
if err != nil {
return err
}
resp, err := c.do("POST", "/services/"+id+"/update?"+queryString(opts), doOptions{
resp, err := c.do(http.MethodPost, "/services/"+id+"/update?"+queryString(opts), doOptions{
headers: headers,
data: opts.ServiceSpec,
forceJSON: true,
@ -127,7 +127,7 @@ func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error {
// See https://goo.gl/dHmr75 for more details.
func (c *Client) InspectService(id string) (*swarm.Service, error) {
path := "/services/" + id
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchService{ID: id}
@ -147,6 +147,7 @@ func (c *Client) InspectService(id string) (*swarm.Service, error) {
// See https://goo.gl/DwvNMd for more details.
type ListServicesOptions struct {
Filters map[string][]string
Status bool
Context context.Context
}
@ -155,7 +156,7 @@ type ListServicesOptions struct {
// See https://goo.gl/DwvNMd for more details.
func (c *Client) ListServices(opts ListServicesOptions) ([]swarm.Service, error) {
path := "/services?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
@ -176,10 +177,10 @@ type LogsServiceOptions struct {
ErrorStream io.Writer `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
Tail string
Since int64
// Use raw terminal? Usually true when the container contains a TTY.
RawTerminal bool `qs:"-"`
Since int64
Follow bool
Stdout bool
Stderr bool
@ -203,7 +204,7 @@ func (c *Client) GetServiceLogs(opts LogsServiceOptions) error {
opts.Tail = "all"
}
path := "/services/" + opts.Service + "/logs?" + queryString(opts)
return c.stream("GET", path, streamOptions{
return c.stream(http.MethodGet, path, streamOptions{
setRawTerminal: opts.RawTerminal,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,

View File

@ -38,7 +38,7 @@ type ListTasksOptions struct {
// See http://goo.gl/rByLzw for more details.
func (c *Client) ListTasks(opts ListTasksOptions) ([]swarm.Task, error) {
path := "/tasks?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
@ -54,7 +54,7 @@ func (c *Client) ListTasks(opts ListTasksOptions) ([]swarm.Task, error) {
//
// See http://goo.gl/kyziuq for more details.
func (c *Client) InspectTask(id string) (*swarm.Task, error) {
resp, err := c.do("GET", "/tasks/"+id, doOptions{})
resp, err := c.do(http.MethodGet, "/tasks/"+id, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchTask{ID: id}

View File

@ -3,6 +3,7 @@ package docker
import (
"context"
"encoding/json"
"net/http"
)
// VolumeUsageData represents usage data from the docker system api
@ -59,7 +60,7 @@ type DiskUsageOptions struct {
// More Info Here https://dockr.ly/2PNzQyO
func (c *Client) DiskUsage(opts DiskUsageOptions) (*DiskUsage, error) {
path := "/system/df"
resp, err := c.do("GET", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -13,8 +13,8 @@ import (
"path/filepath"
"strings"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/fileutils"
"github.com/fsouza/go-dockerclient/internal/archive"
)
func createTarStream(srcPath, dockerfilePath string) (io.ReadCloser, error) {

View File

@ -38,7 +38,7 @@ func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Con
timeout := dialer.Timeout
if !dialer.Deadline.IsZero() {
deadlineTimeout := dialer.Deadline.Sub(time.Now())
deadlineTimeout := time.Until(dialer.Deadline)
if timeout == 0 || deadlineTimeout < timeout {
timeout = deadlineTimeout
}
@ -103,10 +103,9 @@ func copyTLSConfig(cfg *tls.Config) *tls.Config {
ClientCAs: cfg.ClientCAs,
ClientSessionCache: cfg.ClientSessionCache,
CurvePreferences: cfg.CurvePreferences,
InsecureSkipVerify: cfg.InsecureSkipVerify,
InsecureSkipVerify: cfg.InsecureSkipVerify, //nolint:gosec
MaxVersion: cfg.MaxVersion,
MinVersion: cfg.MinVersion,
NameToCertificate: cfg.NameToCertificate,
NextProtos: cfg.NextProtos,
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
Rand: cfg.Rand,

View File

@ -44,7 +44,7 @@ type ListVolumesOptions struct {
//
// See https://goo.gl/3wgTsd for more details.
func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) {
resp, err := c.do("GET", "/volumes?"+queryString(opts), doOptions{
resp, err := c.do(http.MethodGet, "/volumes?"+queryString(opts), doOptions{
context: opts.Context,
})
if err != nil {
@ -85,7 +85,7 @@ type CreateVolumeOptions struct {
//
// See https://goo.gl/qEhmEC for more details.
func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) {
resp, err := c.do("POST", "/volumes/create", doOptions{
resp, err := c.do(http.MethodPost, "/volumes/create", doOptions{
data: opts,
context: opts.Context,
})
@ -104,7 +104,7 @@ func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) {
//
// See https://goo.gl/GMjsMc for more details.
func (c *Client) InspectVolume(name string) (*Volume, error) {
resp, err := c.do("GET", "/volumes/"+name, doOptions{})
resp, err := c.do(http.MethodGet, "/volumes/"+name, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchVolume
@ -142,7 +142,7 @@ type RemoveVolumeOptions struct {
// See https://goo.gl/nvd6qj for more details.
func (c *Client) RemoveVolumeWithOptions(opts RemoveVolumeOptions) error {
path := "/volumes/" + opts.Name
resp, err := c.do("DELETE", path+"?"+queryString(opts), doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path+"?"+queryString(opts), doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
@ -179,7 +179,7 @@ type PruneVolumesResults struct {
// See https://goo.gl/f9XDem 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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

8
vendor/vendor.json vendored
View File

@ -160,10 +160,10 @@
{"path":"github.com/dustin/go-humanize","checksumSHA1":"xteP9Px90oMrg/HZuqvZkpXCR+s=","revision":"8929fe90cee4b2cb9deb468b51fb34eba64d1bf0"},
{"path":"github.com/elazarl/go-bindata-assetfs","checksumSHA1":"7DxViusFRJ7UPH0jZqYatwDrOkY=","revision":"30f82fa23fd844bd5bb1e5f216db87fd77b5eb43","revisionTime":"2017-02-27T21:27:28Z"},
{"path":"github.com/fatih/color","checksumSHA1":"VsE3zx2d8kpwj97TWhYddzAwBrY=","revision":"507f6050b8568533fb3f5504de8e5205fa62a114","revisionTime":"2018-02-13T13:34:03Z"},
{"path":"github.com/fsouza/go-dockerclient","checksumSHA1":"fvbo1J+cFWDrPDs0Yudwv+POIb4=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z"},
{"path":"github.com/fsouza/go-dockerclient/internal/archive","checksumSHA1":"YJ7WR4AVtD2ykYJr+1mTtazkma0=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z"},
{"path":"github.com/fsouza/go-dockerclient/internal/jsonmessage","checksumSHA1":"lnUC8fZCqakWnfuMLUrZD2g+reY=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z"},
{"path":"github.com/fsouza/go-dockerclient/internal/term","checksumSHA1":"vdjeVhnepWyw3H4g9pVTVACDfV0=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z"},
{"path":"github.com/fsouza/go-dockerclient","checksumSHA1":"ep1kI8mdel8vXtOCdVlaLmyvxlA=","revision":"97b4aba9b2565d0313a8a9701626e47b3ef8c490","revisionTime":"2020-02-20T19:25:13Z","version":"v1.6.3","versionExact":"v1.6.3"},
{"path":"github.com/fsouza/go-dockerclient/internal/archive","checksumSHA1":"YJ7WR4AVtD2ykYJr+1mTtazkma0=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z","version":"v1.6.3","versionExact":"v1.6.3"},
{"path":"github.com/fsouza/go-dockerclient/internal/jsonmessage","checksumSHA1":"lnUC8fZCqakWnfuMLUrZD2g+reY=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z","version":"v1.6.3","versionExact":"v1.6.3"},
{"path":"github.com/fsouza/go-dockerclient/internal/term","checksumSHA1":"vdjeVhnepWyw3H4g9pVTVACDfV0=","revision":"01c3e9bd8551d675a60382c0303ef51f5849ea99","revisionTime":"2018-11-29T02:57:25Z","version":"v1.6.3","versionExact":"v1.6.3"},
{"path":"github.com/go-ole/go-ole","checksumSHA1":"IvHj/4iR2nYa/S3cB2GXoyDG/xQ=","comment":"v1.2.0-4-g5005588","revision":"085abb85892dc1949567b726dff00fa226c60c45","revisionTime":"2017-07-12T17:44:59Z"},
{"path":"github.com/go-ole/go-ole/oleutil","checksumSHA1":"qLYVTQDhgrVIeZ2KI9eZV51mmug=","comment":"v1.2.0-4-g5005588","revision":"50055884d646dd9434f16bbb5c9801749b9bafe4"},
{"path":"github.com/godbus/dbus","checksumSHA1":"KSgw53WGLfdZpEMm8It5H0yedV0=","revision":"37bf87eef99d69c4f1d3528bd66e3a87dc201472","revisionTime":"2019-09-30T11:59:46Z","version":"v5","versionExact":"v5.0.3"},