vendors: update fsouza/go-docker-client to v.1.6.3
This commit is contained in:
parent
4d90bf3699
commit
8f57f78087
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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) == "."
|
||||
}
|
|
@ -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"
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
|
|
Loading…
Reference in New Issue