bump containers/image to v5.0.0, buildah to v1.11.4

Move to containers/image v5 and containers/buildah to v1.11.4.

Replace an equality check with a type assertion when checking for a
docker.ErrUnauthorizedForCredentials in `podman login`.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai
2019-10-24 10:37:22 -04:00
parent 59582c55b7
commit a4a70b4506
333 changed files with 10226 additions and 3551 deletions

View File

@ -0,0 +1 @@
* text=auto eol=lf

View File

@ -1,4 +1,2 @@
# temporary symlink for testing
testing/data/symlink
Gopkg.lock
vendor/

View File

@ -0,0 +1,29 @@
run:
deadline: 5m
skip-dirs:
- internal
issues:
exclude-rules:
- path: _test\.go
linters:
- bodyclose
- goconst
- gosec
- scopelint
- path: testing[/\\].+\.go
linters:
- gosec
linters:
enable-all: true
disable:
- dupl
- errcheck
- funlen
- gochecknoglobals
- gocognit
- goconst
- lll
- wsl

View File

@ -1,9 +1,8 @@
dist: xenial
language: go
go:
- 1.11.x
- 1.12.x
- 1.13rc1
- 1.13.x
os:
- linux
- osx
@ -17,7 +16,6 @@ env:
- GO111MODULE=on
install:
- travis-scripts/win-setup.bash
- make testdeps
script:
- travis_wait 25 travis-scripts/run-tests.bash
services:

View File

@ -119,6 +119,7 @@ Kevin Xu
Kim, Hirokuni
Kostas Lekkas
Kyle Allan
Kyle Quest
Yunhee Lee
Liron Levin
Lior Yankovich

View File

@ -1,34 +1,27 @@
.PHONY: \
all \
staticcheck \
lint \
fmt \
fmtcheck \
pretest \
test \
integration
all: test
staticcheck:
GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck
staticcheck ./...
fmtcheck:
if [ -z "$${SKIP_FMT_CHECK}" ]; then [ -z "$$(gofumpt -s -d . | tee /dev/stderr)" ]; fi
lint:
cd /tmp && GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run
fmt:
GO111MODULE=off go get mvdan.cc/gofumpt
gofumpt -s -w .
testdeps:
go mod download
pretest: staticcheck fmtcheck
pretest: lint
gotest:
go test -race -vet all ./...
test: testdeps pretest gotest
test: pretest gotest
integration:
go test -tags docker_integration -run TestIntegration -v

View File

@ -118,7 +118,7 @@ All development commands can be seen in the [Makefile](Makefile).
Commited code must pass:
* [staticcheck](https://staticcheck.io/)
* [golangci-lint](integration_unix_test.go)
* [gofumpt](https://github.com/mvdan/gofumpt)
* [go test](https://golang.org/cmd/go/#hdr-Test_packages)

View File

@ -6,11 +6,9 @@ environment:
GOPATH: c:\gopath
GOPROXY: https://proxy.golang.org
GO111MODULE: on
SKIP_FMT_CHECK: 1
matrix:
- GOVERSION: "1.11.13"
- GOVERSION: "1.12.9"
- GOVERSION: "1.13rc1"
- GOVERSION: "1.12.10"
- GOVERSION: "1.13.1"
install:
- choco install make
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
@ -18,8 +16,8 @@ install:
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.zip
- 7z x go%GOVERSION%.windows-amd64.zip -y -oC:\ > NUL
build_script:
- make testdeps
- make pretest
test_script:
- make pretest gotest
- make gotest
matrix:
fast_finish: true

View File

@ -12,6 +12,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
@ -219,7 +220,7 @@ func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) {
if conf == nil {
return authStatus, errors.New("conf is nil")
}
resp, err := c.do("POST", "/auth", doOptions{data: conf})
resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf})
if err != nil {
return authStatus, err
}

View File

@ -32,8 +32,8 @@ import (
"time"
"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 (
@ -54,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")
@ -269,11 +271,12 @@ func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString stri
// 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(os.Getenv("DOCKER_API_VERSION"))
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
}
@ -397,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
}
@ -409,7 +412,7 @@ 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
}
@ -465,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")
}
@ -520,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 {
@ -529,11 +532,11 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
return err
}
}
return c.streamUrl(method, c.getURL(path), streamOptions)
return c.streamURL(method, c.getURL(path), streamOptions)
}
func (c *Client) streamUrl(method, url string, streamOptions streamOptions) error {
if (method == "POST" || method == "PUT") && streamOptions.in == nil {
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 {
@ -547,7 +550,7 @@ func (c *Client) streamUrl(method, url string, streamOptions streamOptions) erro
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 {
@ -606,6 +609,7 @@ func (c *Client) streamUrl(method, url string, streamOptions streamOptions) erro
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") {
@ -613,11 +617,11 @@ func (c *Client) streamUrl(method, url string, streamOptions streamOptions) erro
}
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)
}
@ -776,9 +780,10 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (Close
errs := make(chan error, 1)
quit := make(chan struct{})
go func() {
//lint:ignore SA1019 this is needed here
//nolint:staticcheck
clientconn := httputil.NewClientConn(dial, nil)
defer clientconn.Close()
//nolint:bodyclose
clientconn.Do(req)
if hijackOptions.success != nil {
hijackOptions.success <- struct{}{}
@ -874,25 +879,26 @@ func (c *Client) getURL(path string) string {
}
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 = ""
}
queryStr, requiredAPIVersion := queryStringVersion(opts)
if c.requestedAPIVersion != nil {
if c.requestedAPIVersion.GreaterThanOrEqualTo(requiredAPIVersion) {
return fmt.Sprintf("%s/v%s%s?%s", urlStr, c.requestedAPIVersion, basepath, queryStr), nil
} else {
return "", fmt.Errorf("API %s requires version %s, requested version %s is insufficient",
basepath, requiredAPIVersion, c.requestedAPIVersion)
}
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
} else {
return fmt.Sprintf("%s%s?%s", urlStr, 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
@ -922,7 +928,7 @@ func queryStringVersion(opts interface{}) (string, APIVersion) {
if value.Kind() != reflect.Struct {
return "", nil
}
var apiVersion APIVersion = nil
var apiVersion APIVersion
items := url.Values(map[string][]string{})
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)
@ -1002,7 +1008,7 @@ func addQueryStringValue(items url.Values, key string, v reflect.Value) bool {
if vLen > 0 {
for i := 0; i < vLen; i++ {
if addQueryStringValue(items, key, v.Index(i)) {
valuesAdded += 1
valuesAdded++
}
}
}

View File

@ -32,7 +32,8 @@ 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)
}

View File

@ -85,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
}
@ -474,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.
@ -500,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,
@ -528,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 {
@ -549,13 +555,14 @@ func (c *Client) InspectContainer(id string) (*Container, error) {
// The context object can be used to cancel the inspect request.
//
// See https://goo.gl/FaI5JT for more details.
//nolint:golint
func (c *Client) InspectContainerWithContext(id string, ctx context.Context) (*Container, error) {
return c.inspectContainer(id, doOptions{context: ctx})
}
func (c *Client) inspectContainer(id string, opts doOptions) (*Container, error) {
path := "/containers/" + id + "/json"
resp, err := c.do("GET", path, opts)
resp, err := c.do(http.MethodGet, path, opts)
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchContainer{ID: id}
@ -575,7 +582,7 @@ func (c *Client) inspectContainer(id string, opts doOptions) (*Container, error)
// 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}
@ -611,7 +618,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 {
@ -729,6 +736,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"`
@ -742,6 +750,8 @@ 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"`
@ -749,6 +759,7 @@ type HostConfig struct {
DeviceCgroupRules []string `json:"DeviceCgroupRules,omitempty" yaml:"DeviceCgroupRules,omitempty" toml:"DeviceCgroupRules,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"`
@ -784,6 +795,8 @@ 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"`
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"`
@ -820,6 +833,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})
}
@ -833,7 +847,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}
@ -860,13 +874,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}
@ -886,7 +901,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}
@ -902,7 +917,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}
@ -918,7 +933,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}
@ -948,7 +963,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}
@ -1116,7 +1131,7 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
reqSent := make(chan struct{})
go func() {
defer close(errC)
err := c.stream("GET", fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{
err := c.stream(http.MethodGet, fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{
rawJSONStream: true,
useJSONDecoder: true,
stdout: writeCloser,
@ -1184,7 +1199,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 {
@ -1225,7 +1240,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}
@ -1254,7 +1269,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,
})
@ -1277,7 +1292,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,
@ -1310,7 +1325,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,
})
@ -1338,12 +1353,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}
@ -1377,7 +1393,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,
})
@ -1412,6 +1428,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:"-"`
@ -1451,7 +1470,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,
@ -1501,7 +1520,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,
@ -1517,7 +1536,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
}
@ -1545,7 +1564,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,
@ -1574,7 +1593,7 @@ type PruneContainersResults struct {
// See https://goo.gl/wnkgDT for more details.
func (c *Client) PruneContainers(opts PruneContainersOptions) (*PruneContainersResults, error) {
path := "/containers/prune?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

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

View File

@ -178,7 +178,7 @@ func (eventState *eventMonitoringState) enableEventMonitoring(c *Client) error {
return nil
}
func (eventState *eventMonitoringState) disableEventMonitoring() error {
func (eventState *eventMonitoringState) disableEventMonitoring() {
eventState.Lock()
defer eventState.Unlock()
@ -191,7 +191,6 @@ func (eventState *eventMonitoringState) disableEventMonitoring() error {
close(eventState.C)
close(eventState.errC)
}
return nil
}
func (eventState *eventMonitoringState) monitorEvents(c *Client) {
@ -330,17 +329,18 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan
if err != nil {
return err
}
//lint:ignore SA1019 this is needed here
//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
}
//lint:ignore SA1019 ClientConn is needed here
//nolint:staticcheck
go func(res *http.Response, conn *httputil.ClientConn) {
defer conn.Close()
defer res.Body.Close()

View File

@ -30,6 +30,7 @@ type CreateExecOptions struct {
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"`
@ -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
}
@ -192,7 +193,7 @@ type ExecInspect struct {
// See https://goo.gl/ctMUiW for more details
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
path := fmt.Sprintf("/exec/%s/json", id)
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchExec{ID: id}

View File

@ -1,26 +1,26 @@
module github.com/fsouza/go-dockerclient
go 1.11
go 1.12
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.14
github.com/Microsoft/hcsshim v0.8.6 // indirect
github.com/containerd/containerd v1.3.0 // indirect
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b
github.com/docker/docker v1.4.2-0.20190927142053-ada3c14355ce
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/protobuf v1.3.0 // indirect
github.com/google/go-cmp v0.3.1
github.com/gorilla/mux v1.7.3
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd
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
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad
google.golang.org/grpc v1.22.0 // indirect
gotest.tools v2.2.0+incompatible // indirect
)

View File

@ -7,14 +7,16 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
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/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.20190710153559-aa8249ae1b8b h1:+Ga+YpCDpcY1fln6GI0fiiirpqHGcob5/Vk3oKNuGdU=
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.4.2-0.20190927142053-ada3c14355ce h1:H3csZuxZESJeeEiOxq4YXPNmLFbjl7u2qVBrAAGX/sA=
github.com/docker/docker v1.4.2-0.20190927142053-ada3c14355ce/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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@ -31,12 +33,12 @@ github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM=
github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84=
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 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=
@ -54,8 +56,8 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -67,9 +69,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

View File

@ -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,
@ -322,12 +323,13 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error
return c.createImage(&opts, headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
}
//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("POST", url, streamOptions{
return c.streamURL(http.MethodPost, url, streamOptions{
setRawTerminal: true,
headers: headers,
in: in,
@ -351,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,
@ -373,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,
@ -403,7 +405,7 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
var err error
var exporturl string
if c.requestedAPIVersion.GreaterThanOrEqualTo(apiVersion125) {
var str string = opts.Names[0]
str := opts.Names[0]
for _, val := range opts.Names[1:] {
str += "," + val
}
@ -412,7 +414,6 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
OutputStream: opts.OutputStream,
InactivityTimeout: opts.InactivityTimeout,
Context: opts.Context,
})
} else {
exporturl, err = c.getPath("/images/get", &opts)
@ -420,7 +421,7 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
if err != nil {
return err
}
return c.streamUrl("GET", exporturl, streamOptions{
return c.streamURL(http.MethodGet, exporturl, streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
@ -471,35 +472,39 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
// https://goo.gl/4nYHwV.
type BuildImageOptions struct {
Context context.Context
Name string `qs:"t"`
Dockerfile string `qs:"dockerfile"`
CacheFrom []string `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"`
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"`
NoCache bool `qs:"nocache"`
SuppressOutput bool `qs:"q"`
Pull bool `qs:"pull"`
RmTmpContainer bool `qs:"rm"`
ForceRmTmpContainer bool `qs:"forcerm"`
RawJSONStream bool `qs:"-"`
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
@ -542,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
}
}
}
@ -557,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
}
}
}
@ -569,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,
@ -610,7 +629,7 @@ 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 {
@ -666,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
}
@ -687,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 {
@ -725,7 +744,7 @@ type PruneImagesResults struct {
// See https://goo.gl/qfZlbZ for more details.
func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) {
path := "/images/prune?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -1,509 +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"
"log"
"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"
)
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
UIDMaps []idtools.IDMap
GIDMaps []idtools.IDMap
ChownOpts *idtools.Identity
// 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.
// For each include when creating an archive, the included name will be
// replaced with the matching name from this map.
RebaseNames map[string]string
NoLchown bool
InUserNS bool
IncludeSourceDir bool
NoOverwriteDirNonDir 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 {
log.Printf("Can't close tar writer: %s", err)
}
if err := compressWriter.Close(); err != nil {
log.Printf("Can't close compress writer: %s", err)
}
if err := pipeWriter.Close(); err != nil {
log.Printf("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 {
log.Print("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 {
include := include
rebaseName := options.RebaseNames[include]
walkRoot := getWalkRoot(srcPath, include)
filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
if err != nil {
log.Printf("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 {
log.Printf("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 {
log.Printf("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
//lint:ignore ST1005 this is vendored/copied code
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
default:
//lint:ignore ST1005 this is vendored/copied code
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 {
//lint:ignore SA1019 this is vendored/copied code
hdr.Xattrs = make(map[string]string)
//lint:ignore SA1019 this is vendored/copied code
hdr.Xattrs["security.capability"] = string(capability)
}
return nil
}
// FileInfoHeader creates a populated Header from fi.
// Compared to archive pkg this function fills in more information.
// Also, regardless of Go version, this function fills file type bits (e.g. hdr.Mode |= modeISDIR),
// which have been deleted since Go 1.9 archive/tar.
func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
hdr, err := tar.FileInfoHeader(fi, link)
if err != nil {
return nil, err
}
hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi)
name, err = canonicalTarName(name, fi.IsDir())
if err != nil {
return nil, fmt.Errorf("tar: cannot canonicalize path: %v", err)
}
hdr.Name = name
if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil {
return nil, err
}
return hdr, nil
}
// fillGo18FileTypeBits fills type bits which have been removed on Go 1.9 archive/tar
// https://github.com/golang/go/commit/66b5a2f
func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
fm := fi.Mode()
switch {
case fm.IsRegular():
mode |= modeISREG
case fi.IsDir():
mode |= modeISDIR
case fm&os.ModeSymlink != 0:
mode |= modeISLNK
case fm&os.ModeDevice != 0:
if fm&os.ModeCharDevice != 0 {
mode |= modeISCHR
} else {
mode |= modeISBLK
}
case fm&os.ModeNamedPipe != 0:
mode |= modeISFIFO
case fm&os.ModeSocket != 0:
mode |= modeISSOCK
}
return mode
}
// canonicalTarName provides a platform-independent and consistent posix-style
// path for files and directories to be archived regardless of the platform.
func canonicalTarName(name string, isDir bool) (string, error) {
name, err := CanonicalTarNameForPath(name)
if err != nil {
return "", err
}
// suffix with '/' for directories
if isDir && !strings.HasSuffix(name, "/") {
name += "/"
}
return name, nil
}

View File

@ -1,106 +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' {
//lint:ignore SA1019 this is vendored/copied code
if hdr.Xattrs != nil {
//lint:ignore SA1019 this is vendored/copied code
delete(hdr.Xattrs, "trusted.overlay.opaque")
}
// create a header for the whiteout file
// it should inherit some properties from the parent, but be a regular file
wo = &tar.Header{
Typeflag: tar.TypeReg,
Mode: hdr.Mode & int64(os.ModePerm),
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir),
Size: 0,
Uid: hdr.Uid,
Uname: hdr.Uname,
Gid: hdr.Gid,
Gname: hdr.Gname,
AccessTime: hdr.AccessTime,
ChangeTime: hdr.ChangeTime,
}
}
}
return
}
func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
base := filepath.Base(path)
dir := filepath.Dir(path)
// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
if base == WhiteoutOpaqueDir {
err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
// don't write the file itself
return false, err
}
// if a file was deleted and we are using overlay, we need to create a character device
if strings.HasPrefix(base, WhiteoutPrefix) {
originalBase := base[len(WhiteoutPrefix):]
originalPath := filepath.Join(dir, originalBase)
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
return false, err
}
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
return false, err
}
// don't write the file itself
return false, nil
}
return true, nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,402 +0,0 @@
// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.
package jsonmessage
import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"time"
units "github.com/docker/go-units"
"github.com/fsouza/go-dockerclient/internal/term"
gotty "github.com/ijc/Gotty"
)
// 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.Fprint(out, endl)
if err != nil {
return err
}
} else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal
return nil
}
if jm.TimeNano != 0 {
_, err := fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed))
if err != nil {
return err
}
} else if jm.Time != 0 {
_, err := fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed))
if err != nil {
return err
}
}
if jm.ID != "" {
_, err := fmt.Fprintf(out, "%s: ", jm.ID)
if err != nil {
return err
}
}
if jm.From != "" {
_, err := fmt.Fprintf(out, "(from %s) ", jm.From)
if err != nil {
return err
}
}
if jm.Progress != nil && termInfo != nil {
_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
if err != nil {
return err
}
} else if jm.ProgressMessage != "" { // deprecated
_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
if err != nil {
return err
}
} else if jm.Stream != "" {
_, err := fmt.Fprintf(out, "%s%s", jm.Stream, endl)
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
if err != nil {
return err
}
}
return nil
}
// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal`
// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of
// each line and move the cursor while displaying.
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
var (
dec = json.NewDecoder(in)
ids = make(map[string]int)
)
var termInfo termInfo
if isTerminal {
term := os.Getenv("TERM")
if term == "" {
term = "vt102"
}
var err error
if termInfo, err = gotty.OpenTermInfo(term); err != nil {
termInfo = &noTermInfo{}
}
}
for {
diff := 0
var jm JSONMessage
if err := dec.Decode(&jm); err != nil {
if err == io.EOF {
break
}
return err
}
if jm.Aux != nil {
if auxCallback != nil {
auxCallback(jm)
}
continue
}
if jm.Progress != nil {
jm.Progress.terminalFd = terminalFd
}
if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
line, ok := ids[jm.ID]
if !ok {
// NOTE: This approach of using len(id) to
// figure out the number of lines of history
// only works as long as we clear the history
// when we output something that's not
// accounted for in the map, such as a line
// with no ID.
line = len(ids)
ids[jm.ID] = line
if termInfo != nil {
_, err := fmt.Fprintf(out, "\n")
if err != nil {
return err
}
}
}
diff = len(ids) - line
if termInfo != nil {
if err := cursorUp(out, termInfo, diff); err != nil {
return err
}
}
} else {
// When outputting something that isn't progress
// output, clear the history of previous lines. We
// don't want progress entries from some previous
// operation to be updated (for example, pull -a
// with multiple tags).
ids = make(map[string]int)
}
err := jm.Display(out, termInfo)
if jm.ID != "" && termInfo != nil {
if err := cursorDown(out, termInfo, diff); err != nil {
return err
}
}
if err != nil {
return err
}
}
return nil
}
type stream interface {
io.Writer
FD() uintptr
IsTerminal() bool
}
// DisplayJSONMessagesToStream prints json messages to the output stream
func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(JSONMessage)) error {
return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback)
}

View File

@ -1,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 term
// Winsize represents the size of the terminal window.
type Winsize struct {
Height uint16
Width uint16
}

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import (
"context"
"encoding/json"
"net"
"net/http"
"strings"
"github.com/docker/docker/api/types/swarm"
@ -22,7 +23,7 @@ func (c *Client) Version() (*Env, error) {
// 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
@ -162,7 +164,7 @@ type IndexInfo struct {
//
// See https://goo.gl/ElTHi2 for more details.
func (c *Client) Info() (*DockerInfo, error) {
resp, err := c.do("GET", "/info", doOptions{})
resp, err := c.do(http.MethodGet, "/info", doOptions{})
if err != nil {
return nil, err
}

View File

@ -48,7 +48,7 @@ type Endpoint struct {
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) ListNetworks() ([]Network, error) {
resp, err := c.do("GET", "/networks", doOptions{})
resp, err := c.do(http.MethodGet, "/networks", doOptions{})
if err != nil {
return nil, err
}
@ -75,7 +75,7 @@ func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error)
qs := make(url.Values)
qs.Add("filters", string(params))
path := "/networks?" + qs.Encode()
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
return nil, err
}
@ -92,7 +92,7 @@ func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error)
// See https://goo.gl/6GugX3 for more details.
func (c *Client) NetworkInfo(id string) (*Network, error) {
path := "/networks/" + id
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchNetwork{ID: id}
@ -159,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,
@ -193,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}
@ -253,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,
})
@ -272,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}
@ -303,7 +303,7 @@ type PruneNetworksResults struct {
// See https://goo.gl/kX0S9h for more details.
func (c *Client) PruneNetworks(opts PruneNetworksOptions) (*PruneNetworksResults, error) {
path := "/networks/prune?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}

View File

@ -35,15 +35,26 @@ type InstallPluginOptions struct {
//
// See https://goo.gl/C4t7Tz for more details.
func (c *Client) InstallPlugins(opts InstallPluginOptions) error {
headers, err := headersWithAuth(opts.Auth)
if err != nil {
return err
}
path := "/plugins/pull?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Plugins,
context: opts.Context,
headers: headers,
})
if err != nil {
return err
}
resp.Body.Close()
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,7 +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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return err
}
@ -310,7 +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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return err
}
@ -335,7 +378,7 @@ 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,
})
@ -365,7 +408,7 @@ 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})
resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
if err != nil {
return err
}
@ -389,7 +432,7 @@ 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,
})

View File

@ -36,7 +36,7 @@ type InitSwarmOptions struct {
// See https://goo.gl/ZWyG1M for more details.
func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) {
path := "/swarm/init"
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.InitRequest,
forceJSON: true,
context: opts.Context,
@ -66,7 +66,7 @@ type JoinSwarmOptions struct {
// See https://goo.gl/N59IP1 for more details.
func (c *Client) JoinSwarm(opts JoinSwarmOptions) error {
path := "/swarm/join"
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.JoinRequest,
forceJSON: true,
context: opts.Context,
@ -93,7 +93,7 @@ func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error {
params := make(url.Values)
params.Set("force", strconv.FormatBool(opts.Force))
path := "/swarm/leave?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
context: opts.Context,
})
if err != nil {
@ -123,7 +123,7 @@ func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error {
params.Set("rotateWorkerToken", strconv.FormatBool(opts.RotateWorkerToken))
params.Set("rotateManagerToken", strconv.FormatBool(opts.RotateManagerToken))
path := "/swarm/update?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
data: opts.Swarm,
forceJSON: true,
context: opts.Context,
@ -141,7 +141,7 @@ func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error {
// See https://goo.gl/MFwgX9 for more details.
func (c *Client) InspectSwarm(ctx context.Context) (swarm.Swarm, error) {
response := swarm.Swarm{}
resp, err := c.do("GET", "/swarm", doOptions{
resp, err := c.do(http.MethodGet, "/swarm", doOptions{
context: ctx,
})
if err != nil {

View File

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

View File

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

View File

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

View File

@ -46,7 +46,7 @@ func (c *Client) CreateService(opts CreateServiceOptions) (*swarm.Service, error
return nil, err
}
path := "/services/create?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
resp, err := c.do(http.MethodPost, path, doOptions{
headers: headers,
data: opts.ServiceSpec,
forceJSON: true,
@ -76,7 +76,7 @@ type RemoveServiceOptions struct {
// See https://goo.gl/Tqrtya for more details.
func (c *Client) RemoveService(opts RemoveServiceOptions) error {
path := "/services/" + opts.ID
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchService{ID: opts.ID}
@ -106,7 +106,7 @@ func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error {
if err != nil {
return err
}
resp, err := c.do("POST", "/services/"+id+"/update?"+queryString(opts), doOptions{
resp, err := c.do(http.MethodPost, "/services/"+id+"/update?"+queryString(opts), doOptions{
headers: headers,
data: opts.ServiceSpec,
forceJSON: true,
@ -127,7 +127,7 @@ func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error {
// See https://goo.gl/dHmr75 for more details.
func (c *Client) InspectService(id string) (*swarm.Service, error) {
path := "/services/" + id
resp, err := c.do("GET", path, doOptions{})
resp, err := c.do(http.MethodGet, path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchService{ID: id}
@ -155,7 +155,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
}
@ -203,7 +203,7 @@ func (c *Client) GetServiceLogs(opts LogsServiceOptions) error {
opts.Tail = "all"
}
path := "/services/" + opts.Service + "/logs?" + queryString(opts)
return c.stream("GET", path, streamOptions{
return c.stream(http.MethodGet, path, streamOptions{
setRawTerminal: opts.RawTerminal,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,

View File

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

View File

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

View File

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

View File

@ -103,7 +103,7 @@ 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,

View File

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