Merge pull request #1224 from crazy-max/update-docker-cli

vendor: update docker/cli to f1615fa
pull/1235/head
Tõnis Tiigi 3 years ago committed by GitHub
commit 9ff5fb0356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,7 +18,7 @@ import (
"github.com/docker/buildx/util/buildflags" "github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/platformutil"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
"github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/builder/remotecontext/urlutil"
hcl "github.com/hashicorp/hcl/v2" hcl "github.com/hashicorp/hcl/v2"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/session/auth/authprovider"

@ -30,9 +30,9 @@ import (
"github.com/docker/cli/opts" "github.com/docker/cli/opts"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/builder/remotecontext/urlutil"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/urlutil"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes" "github.com/moby/buildkit/exporter/containerimage/exptypes"

@ -309,7 +309,7 @@ func (d *Driver) Stop(ctx context.Context, force bool) error {
return err return err
} }
if info.Status == driver.Running { if info.Status == driver.Running {
return d.DockerAPI.ContainerStop(ctx, d.Name, nil) return d.DockerAPI.ContainerStop(ctx, d.Name, container.StopOptions{})
} }
return nil return nil
} }

@ -6,10 +6,10 @@ require (
github.com/compose-spec/compose-go v1.2.7 github.com/compose-spec/compose-go v1.2.7
github.com/containerd/console v1.0.3 github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.6.6 github.com/containerd/containerd v1.6.6
github.com/docker/cli v20.10.14+incompatible github.com/docker/cli v20.10.17+incompatible // v22.06.x - see "replace" for the actual version
github.com/docker/cli-docs-tool v0.4.0 github.com/docker/cli-docs-tool v0.4.0
github.com/docker/distribution v2.8.1+incompatible github.com/docker/distribution v2.8.1+incompatible
github.com/docker/docker v20.10.7+incompatible github.com/docker/docker v20.10.17+incompatible // v22.06.x - see "replace" for the actual version
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/gofrs/flock v0.7.3 github.com/gofrs/flock v0.7.3
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
@ -23,7 +23,7 @@ require (
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.2.1 github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/zclconf/go-cty v1.10.0 github.com/zclconf/go-cty v1.10.0
@ -97,8 +97,6 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/locker v1.0.1 // indirect github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/mount v0.3.0 // indirect
github.com/moby/sys/mountinfo v0.6.0 // indirect
github.com/moby/sys/signal v0.6.0 // indirect github.com/moby/sys/signal v0.6.0 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@ -150,8 +148,8 @@ require (
) )
replace ( replace (
github.com/docker/cli => github.com/docker/cli v20.10.3-0.20220226190722-8667ccd1124c+incompatible github.com/docker/cli => github.com/docker/cli v20.10.3-0.20220721163225-f1615facb1ca+incompatible // master (v22.06-dev)
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220720171342-a60b458179aa+incompatible // 22.06 branch (v22.06-dev)
k8s.io/api => k8s.io/api v0.22.4 k8s.io/api => k8s.io/api v0.22.4
k8s.io/apimachinery => k8s.io/apimachinery v0.22.4 k8s.io/apimachinery => k8s.io/apimachinery v0.22.4
k8s.io/client-go => k8s.io/client-go v0.22.4 k8s.io/client-go => k8s.io/client-go v0.22.4

@ -152,6 +152,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -167,14 +168,14 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e h1:n81KvOMrLZa+VWHwST7dun9f0G98X3zREHS1ztYzZKU= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e h1:n81KvOMrLZa+VWHwST7dun9f0G98X3zREHS1ztYzZKU=
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v20.10.3-0.20220226190722-8667ccd1124c+incompatible h1:0Kr33P67t7g42iFMU3uzizk+bek+a5MThEqmt4bqVGM= github.com/docker/cli v20.10.3-0.20220721163225-f1615facb1ca+incompatible h1:Dd/CSOpM6U0thw3xNPlw6m+5/4VOexEcgKlL38haGgk=
github.com/docker/cli v20.10.3-0.20220226190722-8667ccd1124c+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.3-0.20220721163225-f1615facb1ca+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.4.0 h1:MdfKoErGEbFqIxQ8an9BsZ+YzKUGd58RBVkV+Q82GPo= github.com/docker/cli-docs-tool v0.4.0 h1:MdfKoErGEbFqIxQ8an9BsZ+YzKUGd58RBVkV+Q82GPo=
github.com/docker/cli-docs-tool v0.4.0/go.mod h1:rgW5KKdNpLMBIuH4WQ/1RNh38nH+/Ay5jgL4P0ZMPpY= github.com/docker/cli-docs-tool v0.4.0/go.mod h1:rgW5KKdNpLMBIuH4WQ/1RNh38nH+/Ay5jgL4P0ZMPpY=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible h1:Ptj2To+ezU/mCBUKdYXBQ2r3/2EJojAlOZrsgprF+is= github.com/docker/docker v20.10.3-0.20220720171342-a60b458179aa+incompatible h1:gaceOC7utpmWqA7OF95kxXO2lGNTkbHIvgxpE+0hPi8=
github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.3-0.20220720171342-a60b458179aa+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@ -464,11 +465,8 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mount v0.3.0 h1:bXZYMmq7DBQPwHRxH/MG+u9+XF90ZOwoXpHTOznMGp0=
github.com/moby/sys/mount v0.3.0/go.mod h1:U2Z3ur2rXPFrFmy4q6WMwWrBOAQGYtYTRVM8BIvzbwk=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/sys/mountinfo v0.6.0 h1:gUDhXQx58YNrpHlK4nSL+7y2pxFZkUcXqzFDKWdC0Oo= github.com/moby/sys/mountinfo v0.6.0 h1:gUDhXQx58YNrpHlK4nSL+7y2pxFZkUcXqzFDKWdC0Oo=
github.com/moby/sys/mountinfo v0.6.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/sys/signal v0.6.0 h1:aDpY94H8VlhTGa9sNYUFCFsMZIUh5wm0B6XkIoJj/iY= github.com/moby/sys/signal v0.6.0 h1:aDpY94H8VlhTGa9sNYUFCFsMZIUh5wm0B6XkIoJj/iY=
github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
@ -563,6 +561,7 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@ -591,8 +590,9 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=

@ -3,7 +3,7 @@
Aanand Prasad <aanand.prasad@gmail.com> Aanand Prasad <aanand.prasad@gmail.com>
Aaron L. Xu <liker.xu@foxmail.com> Aaron L. Xu <liker.xu@foxmail.com>
Aaron Lehmann <aaron.lehmann@docker.com> Aaron Lehmann <alehmann@netflix.com>
Aaron.L.Xu <likexu@harmonycloud.cn> Aaron.L.Xu <likexu@harmonycloud.cn>
Abdur Rehman <abdur_rehman@mentor.com> Abdur Rehman <abdur_rehman@mentor.com>
Abhinandan Prativadi <abhi@docker.com> Abhinandan Prativadi <abhi@docker.com>
@ -28,18 +28,21 @@ Albin Kerouanton <albin@akerouanton.name>
Aleksa Sarai <asarai@suse.de> Aleksa Sarai <asarai@suse.de>
Aleksander Piotrowski <apiotrowski312@gmail.com> Aleksander Piotrowski <apiotrowski312@gmail.com>
Alessandro Boch <aboch@tetrationanalytics.com> Alessandro Boch <aboch@tetrationanalytics.com>
Alex Couture-Beil <alex@earthly.dev>
Alex Mavrogiannis <alex.mavrogiannis@docker.com> Alex Mavrogiannis <alex.mavrogiannis@docker.com>
Alex Mayer <amayer5125@gmail.com> Alex Mayer <amayer5125@gmail.com>
Alexander Boyd <alex@opengroove.org> Alexander Boyd <alex@opengroove.org>
Alexander Larsson <alexl@redhat.com> Alexander Larsson <alexl@redhat.com>
Alexander Morozov <lk4d4@docker.com> Alexander Morozov <lk4d4math@gmail.com>
Alexander Ryabov <i@sepa.spb.ru> Alexander Ryabov <i@sepa.spb.ru>
Alexandre González <agonzalezro@gmail.com> Alexandre González <agonzalezro@gmail.com>
Alexey Igrychev <alexey.igrychev@flant.com>
Alfred Landrum <alfred.landrum@docker.com> Alfred Landrum <alfred.landrum@docker.com>
Alicia Lauerman <alicia@eta.im> Alicia Lauerman <alicia@eta.im>
Allen Sun <allensun.shl@alibaba-inc.com> Allen Sun <allensun.shl@alibaba-inc.com>
Alvin Deng <alvin.q.deng@utexas.edu> Alvin Deng <alvin.q.deng@utexas.edu>
Amen Belayneh <amenbelayneh@gmail.com> Amen Belayneh <amenbelayneh@gmail.com>
Amey Shrivastava <72866602+AmeyShrivastava@users.noreply.github.com>
Amir Goldstein <amir73il@aquasec.com> Amir Goldstein <amir73il@aquasec.com>
Amit Krishnan <amit.krishnan@oracle.com> Amit Krishnan <amit.krishnan@oracle.com>
Amit Shukla <amit.shukla@docker.com> Amit Shukla <amit.shukla@docker.com>
@ -48,6 +51,8 @@ Anca Iordache <anca.iordache@docker.com>
Anda Xu <anda.xu@docker.com> Anda Xu <anda.xu@docker.com>
Andrea Luzzardi <aluzzardi@gmail.com> Andrea Luzzardi <aluzzardi@gmail.com>
Andreas Köhler <andi5.py@gmx.net> Andreas Köhler <andi5.py@gmx.net>
Andres G. Aragoneses <knocte@gmail.com>
Andres Leon Rangel <aleon1220@gmail.com>
Andrew France <andrew@avito.co.uk> Andrew France <andrew@avito.co.uk>
Andrew Hsu <andrewhsu@docker.com> Andrew Hsu <andrewhsu@docker.com>
Andrew Macpherson <hopscotch23@gmail.com> Andrew Macpherson <hopscotch23@gmail.com>
@ -67,8 +72,9 @@ Antonis Kalipetis <akalipetis@gmail.com>
Anusha Ragunathan <anusha.ragunathan@docker.com> Anusha Ragunathan <anusha.ragunathan@docker.com>
Ao Li <la9249@163.com> Ao Li <la9249@163.com>
Arash Deshmeh <adeshmeh@ca.ibm.com> Arash Deshmeh <adeshmeh@ca.ibm.com>
Arko Dasgupta <arko.dasgupta@docker.com> Arko Dasgupta <arko@tetrate.io>
Arnaud Porterie <arnaud.porterie@docker.com> Arnaud Porterie <icecrime@gmail.com>
Arnaud Rebillout <elboulangero@gmail.com>
Arthur Peka <arthur.peka@outlook.com> Arthur Peka <arthur.peka@outlook.com>
Ashwini Oruganti <ashwini.oruganti@gmail.com> Ashwini Oruganti <ashwini.oruganti@gmail.com>
Azat Khuyiyakhmetov <shadow_uz@mail.ru> Azat Khuyiyakhmetov <shadow_uz@mail.ru>
@ -80,6 +86,8 @@ Ben Bonnefoy <frenchben@docker.com>
Ben Creasy <ben@bencreasy.com> Ben Creasy <ben@bencreasy.com>
Ben Firshman <ben@firshman.co.uk> Ben Firshman <ben@firshman.co.uk>
Benjamin Boudreau <boudreau.benjamin@gmail.com> Benjamin Boudreau <boudreau.benjamin@gmail.com>
Benjamin Böhmke <benjamin@boehmke.net>
Benjamin Nater <me@bn4t.me>
Benoit Sigoure <tsunanet@gmail.com> Benoit Sigoure <tsunanet@gmail.com>
Bhumika Bayani <bhumikabayani@gmail.com> Bhumika Bayani <bhumikabayani@gmail.com>
Bill Wang <ozbillwang@gmail.com> Bill Wang <ozbillwang@gmail.com>
@ -88,6 +96,7 @@ Bingshen Wang <bingshen.wbs@alibaba-inc.com>
Boaz Shuster <ripcurld.github@gmail.com> Boaz Shuster <ripcurld.github@gmail.com>
Bogdan Anton <contact@bogdananton.ro> Bogdan Anton <contact@bogdananton.ro>
Boris Pruessmann <boris@pruessmann.org> Boris Pruessmann <boris@pruessmann.org>
Brad Baker <brad@brad.fi>
Bradley Cicenas <bradley.cicenas@gmail.com> Bradley Cicenas <bradley.cicenas@gmail.com>
Brandon Mitchell <git@bmitch.net> Brandon Mitchell <git@bmitch.net>
Brandon Philips <brandon.philips@coreos.com> Brandon Philips <brandon.philips@coreos.com>
@ -114,15 +123,19 @@ Charles Chan <charleswhchan@users.noreply.github.com>
Charles Law <claw@conduce.com> Charles Law <claw@conduce.com>
Charles Smith <charles.smith@docker.com> Charles Smith <charles.smith@docker.com>
Charlie Drage <charlie@charliedrage.com> Charlie Drage <charlie@charliedrage.com>
Charlotte Mach <charlotte.mach@fs.lmu.de>
ChaYoung You <yousbe@gmail.com> ChaYoung You <yousbe@gmail.com>
Chee Hau Lim <cheehau.lim@mobimeo.com>
Chen Chuanliang <chen.chuanliang@zte.com.cn> Chen Chuanliang <chen.chuanliang@zte.com.cn>
Chen Hanxiao <chenhanxiao@cn.fujitsu.com> Chen Hanxiao <chenhanxiao@cn.fujitsu.com>
Chen Mingjie <chenmingjie0828@163.com> Chen Mingjie <chenmingjie0828@163.com>
Chen Qiu <cheney-90@hotmail.com> Chen Qiu <cheney-90@hotmail.com>
Chris Couzens <ccouzens@gmail.com>
Chris Gavin <chris@chrisgavin.me> Chris Gavin <chris@chrisgavin.me>
Chris Gibson <chris@chrisg.io> Chris Gibson <chris@chrisg.io>
Chris McKinnel <chrismckinnel@gmail.com> Chris McKinnel <chrismckinnel@gmail.com>
Chris Snow <chsnow123@gmail.com> Chris Snow <chsnow123@gmail.com>
Chris Vermilion <christopher.vermilion@gmail.com>
Chris Weyl <cweyl@alumni.drew.edu> Chris Weyl <cweyl@alumni.drew.edu>
Christian Persson <saser@live.se> Christian Persson <saser@live.se>
Christian Stefanescu <st.chris@gmail.com> Christian Stefanescu <st.chris@gmail.com>
@ -131,6 +144,7 @@ Christophe Vidal <kriss@krizalys.com>
Christopher Biscardi <biscarch@sketcht.com> Christopher Biscardi <biscarch@sketcht.com>
Christopher Crone <christopher.crone@docker.com> Christopher Crone <christopher.crone@docker.com>
Christopher Jones <tophj@linux.vnet.ibm.com> Christopher Jones <tophj@linux.vnet.ibm.com>
Christopher Svensson <stoffus@stoffus.com>
Christy Norman <christy@linux.vnet.ibm.com> Christy Norman <christy@linux.vnet.ibm.com>
Chun Chen <ramichen@tencent.com> Chun Chen <ramichen@tencent.com>
Clinton Kitson <clintonskitson@gmail.com> Clinton Kitson <clintonskitson@gmail.com>
@ -139,8 +153,10 @@ Colin Hebert <hebert.colin@gmail.com>
Collin Guarino <collin.guarino@gmail.com> Collin Guarino <collin.guarino@gmail.com>
Colm Hally <colmhally@gmail.com> Colm Hally <colmhally@gmail.com>
Comical Derskeal <27731088+derskeal@users.noreply.github.com> Comical Derskeal <27731088+derskeal@users.noreply.github.com>
Conner Crosby <conner@cavcrosby.tech>
Corey Farrell <git@cfware.com> Corey Farrell <git@cfware.com>
Corey Quon <corey.quon@docker.com> Corey Quon <corey.quon@docker.com>
Cory Bennet <cbennett@netflix.com>
Craig Wilhite <crwilhit@microsoft.com> Craig Wilhite <crwilhit@microsoft.com>
Cristian Staretu <cristian.staretu@gmail.com> Cristian Staretu <cristian.staretu@gmail.com>
Daehyeok Mun <daehyeok@gmail.com> Daehyeok Mun <daehyeok@gmail.com>
@ -170,6 +186,7 @@ Dattatraya Kumbhar <dattatraya.kumbhar@gslab.com>
Dave Goodchild <buddhamagnet@gmail.com> Dave Goodchild <buddhamagnet@gmail.com>
Dave Henderson <dhenderson@gmail.com> Dave Henderson <dhenderson@gmail.com>
Dave Tucker <dt@docker.com> Dave Tucker <dt@docker.com>
David Alvarez <david.alvarez@flyeralarm.com>
David Beitey <david@davidjb.com> David Beitey <david@davidjb.com>
David Calavera <david.calavera@gmail.com> David Calavera <david.calavera@gmail.com>
David Cramer <davcrame@cisco.com> David Cramer <davcrame@cisco.com>
@ -186,7 +203,8 @@ Denis Defreyne <denis@soundcloud.com>
Denis Gladkikh <denis@gladkikh.email> Denis Gladkikh <denis@gladkikh.email>
Denis Ollier <larchunix@users.noreply.github.com> Denis Ollier <larchunix@users.noreply.github.com>
Dennis Docter <dennis@d23.nl> Dennis Docter <dennis@d23.nl>
Derek McGowan <derek@mcgstyle.net> Derek McGowan <derek@mcg.dev>
Des Preston <despreston@gmail.com>
Deshi Xiao <dxiao@redhat.com> Deshi Xiao <dxiao@redhat.com>
Dharmit Shah <shahdharmit@gmail.com> Dharmit Shah <shahdharmit@gmail.com>
Dhawal Yogesh Bhanushali <dbhanushali@vmware.com> Dhawal Yogesh Bhanushali <dbhanushali@vmware.com>
@ -196,12 +214,14 @@ Dimitry Andric <d.andric@activevideo.com>
Ding Fei <dingfei@stars.org.cn> Ding Fei <dingfei@stars.org.cn>
Diogo Monica <diogo@docker.com> Diogo Monica <diogo@docker.com>
Djordje Lukic <djordje.lukic@docker.com> Djordje Lukic <djordje.lukic@docker.com>
Dmitriy Fishman <fishman.code@gmail.com>
Dmitry Gusev <dmitry.gusev@gmail.com> Dmitry Gusev <dmitry.gusev@gmail.com>
Dmitry Smirnov <onlyjob@member.fsf.org> Dmitry Smirnov <onlyjob@member.fsf.org>
Dmitry V. Krivenok <krivenok.dmitry@gmail.com> Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
Dominik Braun <dominik.braun@nbsp.de> Dominik Braun <dominik.braun@nbsp.de>
Don Kjer <don.kjer@gmail.com> Don Kjer <don.kjer@gmail.com>
Dong Chen <dongluo.chen@docker.com> Dong Chen <dongluo.chen@docker.com>
DongGeon Lee <secmatth1996@gmail.com>
Doug Davis <dug@us.ibm.com> Doug Davis <dug@us.ibm.com>
Drew Erny <derny@mirantis.com> Drew Erny <derny@mirantis.com>
Ed Costello <epc@epcostello.com> Ed Costello <epc@epcostello.com>
@ -211,12 +231,14 @@ Eli Uriegas <seemethere101@gmail.com>
Elias Faxö <elias.faxo@tre.se> Elias Faxö <elias.faxo@tre.se>
Elliot Luo <956941328@qq.com> Elliot Luo <956941328@qq.com>
Eric Curtin <ericcurtin17@gmail.com> Eric Curtin <ericcurtin17@gmail.com>
Eric Engestrom <eric@engestrom.ch>
Eric G. Noriega <enoriega@vizuri.com> Eric G. Noriega <enoriega@vizuri.com>
Eric Rosenberg <ehaydenr@gmail.com> Eric Rosenberg <ehaydenr@gmail.com>
Eric Sage <eric.david.sage@gmail.com> Eric Sage <eric.david.sage@gmail.com>
Eric-Olivier Lamey <eo@lamey.me> Eric-Olivier Lamey <eo@lamey.me>
Erica Windisch <erica@windisch.us> Erica Windisch <erica@windisch.us>
Erik Hollensbe <github@hollensbe.org> Erik Hollensbe <github@hollensbe.org>
Erik Humphrey <erik.humphrey@carleton.ca>
Erik St. Martin <alakriti@gmail.com> Erik St. Martin <alakriti@gmail.com>
Essam A. Hassan <es.hassan187@gmail.com> Essam A. Hassan <es.hassan187@gmail.com>
Ethan Haynes <ethanhaynes@alumni.harvard.edu> Ethan Haynes <ethanhaynes@alumni.harvard.edu>
@ -231,6 +253,7 @@ Fabio Falci <fabiofalci@gmail.com>
Fabrizio Soppelsa <fsoppelsa@mirantis.com> Fabrizio Soppelsa <fsoppelsa@mirantis.com>
Felix Hupfeld <felix@quobyte.com> Felix Hupfeld <felix@quobyte.com>
Felix Rabe <felix@rabe.io> Felix Rabe <felix@rabe.io>
fezzik1620 <fezzik1620@users.noreply.github.com>
Filip Jareš <filipjares@gmail.com> Filip Jareš <filipjares@gmail.com>
Flavio Crisciani <flavio.crisciani@docker.com> Flavio Crisciani <flavio.crisciani@docker.com>
Florian Klein <florian.klein@free.fr> Florian Klein <florian.klein@free.fr>
@ -242,6 +265,7 @@ Frederic Hemberger <mail@frederic-hemberger.de>
Frederick F. Kautz IV <fkautz@redhat.com> Frederick F. Kautz IV <fkautz@redhat.com>
Frederik Nordahl Jul Sabroe <frederikns@gmail.com> Frederik Nordahl Jul Sabroe <frederikns@gmail.com>
Frieder Bluemle <frieder.bluemle@gmail.com> Frieder Bluemle <frieder.bluemle@gmail.com>
Gabriel Gore <gabgore@cisco.com>
Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com> Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com>
Gaetan de Villele <gdevillele@gmail.com> Gaetan de Villele <gdevillele@gmail.com>
Gang Qiao <qiaohai8866@gmail.com> Gang Qiao <qiaohai8866@gmail.com>
@ -251,13 +275,17 @@ George MacRorie <gmacr31@gmail.com>
George Xie <georgexsh@gmail.com> George Xie <georgexsh@gmail.com>
Gianluca Borello <g.borello@gmail.com> Gianluca Borello <g.borello@gmail.com>
Gildas Cuisinier <gildas.cuisinier@gcuisinier.net> Gildas Cuisinier <gildas.cuisinier@gcuisinier.net>
Gleb Stsenov <gleb.stsenov@gmail.com>
Goksu Toprak <goksu.toprak@docker.com> Goksu Toprak <goksu.toprak@docker.com>
Gou Rao <gou@portworx.com> Gou Rao <gou@portworx.com>
Govind Rai <raigovind93@gmail.com>
Grant Reaber <grant.reaber@gmail.com> Grant Reaber <grant.reaber@gmail.com>
Greg Pflaum <gpflaum@users.noreply.github.com> Greg Pflaum <gpflaum@users.noreply.github.com>
Gsealy <jiaojingwei1001@hotmail.com>
Guilhem Lettron <guilhem+github@lettron.fr> Guilhem Lettron <guilhem+github@lettron.fr>
Guillaume J. Charmes <guillaume.charmes@docker.com> Guillaume J. Charmes <guillaume.charmes@docker.com>
Guillaume Le Floch <glfloch@gmail.com> Guillaume Le Floch <glfloch@gmail.com>
Guillaume Tardif <guillaume.tardif@gmail.com>
gwx296173 <gaojing3@huawei.com> gwx296173 <gaojing3@huawei.com>
Günther Jungbluth <gunther@gameslabs.net> Günther Jungbluth <gunther@gameslabs.net>
Hakan Özler <hakan.ozler@kodcu.com> Hakan Özler <hakan.ozler@kodcu.com>
@ -278,6 +306,7 @@ Hugo Gabriel Eyherabide <hugogabriel.eyherabide@gmail.com>
huqun <huqun@zju.edu.cn> huqun <huqun@zju.edu.cn>
Huu Nguyen <huu@prismskylabs.com> Huu Nguyen <huu@prismskylabs.com>
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com> Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com>
Iain Samuel McLean Elder <iain@isme.es>
Ian Campbell <ian.campbell@docker.com> Ian Campbell <ian.campbell@docker.com>
Ian Philpot <ian.philpot@microsoft.com> Ian Philpot <ian.philpot@microsoft.com>
Ignacio Capurro <icapurrofagian@gmail.com> Ignacio Capurro <icapurrofagian@gmail.com>
@ -287,6 +316,7 @@ Ilya Sotkov <ilya@sotkov.com>
Ioan Eugen Stan <eu@ieugen.ro> Ioan Eugen Stan <eu@ieugen.ro>
Isabel Jimenez <contact.isabeljimenez@gmail.com> Isabel Jimenez <contact.isabeljimenez@gmail.com>
Ivan Grcic <igrcic@gmail.com> Ivan Grcic <igrcic@gmail.com>
Ivan Grund <ivan.grund@gmail.com>
Ivan Markin <sw@nogoegst.net> Ivan Markin <sw@nogoegst.net>
Jacob Atzen <jacob@jacobatzen.dk> Jacob Atzen <jacob@jacobatzen.dk>
Jacob Tomlinson <jacob@tom.linson.uk> Jacob Tomlinson <jacob@tom.linson.uk>
@ -305,12 +335,14 @@ Jasmine Hegman <jasmine@jhegman.com>
Jason Heiss <jheiss@aput.net> Jason Heiss <jheiss@aput.net>
Jason Plum <jplum@devonit.com> Jason Plum <jplum@devonit.com>
Jay Kamat <github@jgkamat.33mail.com> Jay Kamat <github@jgkamat.33mail.com>
Jean Lecordier <jeanlecordier@hotmail.fr>
Jean Rouge <rougej+github@gmail.com> Jean Rouge <rougej+github@gmail.com>
Jean-Christophe Sirot <jean-christophe.sirot@docker.com> Jean-Christophe Sirot <jean-christophe.sirot@docker.com>
Jean-Pierre Huynh <jean-pierre.huynh@ounet.fr> Jean-Pierre Huynh <jean-pierre.huynh@ounet.fr>
Jeff Lindsay <progrium@gmail.com> Jeff Lindsay <progrium@gmail.com>
Jeff Nickoloff <jeff.nickoloff@gmail.com> Jeff Nickoloff <jeff.nickoloff@gmail.com>
Jeff Silberman <jsilberm@gmail.com> Jeff Silberman <jsilberm@gmail.com>
Jennings Zhang <jenni_zh@protonmail.com>
Jeremy Chambers <jeremy@thehipbot.com> Jeremy Chambers <jeremy@thehipbot.com>
Jeremy Unruh <jeremybunruh@gmail.com> Jeremy Unruh <jeremybunruh@gmail.com>
Jeremy Yallop <yallop@docker.com> Jeremy Yallop <yallop@docker.com>
@ -322,6 +354,7 @@ Jian Zhang <zhangjian.fnst@cn.fujitsu.com>
Jie Luo <luo612@zju.edu.cn> Jie Luo <luo612@zju.edu.cn>
Jilles Oldenbeuving <ojilles@gmail.com> Jilles Oldenbeuving <ojilles@gmail.com>
Jim Galasyn <jim.galasyn@docker.com> Jim Galasyn <jim.galasyn@docker.com>
Jim Lin <b04705003@ntu.edu.tw>
Jimmy Leger <jimmy.leger@gmail.com> Jimmy Leger <jimmy.leger@gmail.com>
Jimmy Song <rootsongjc@gmail.com> Jimmy Song <rootsongjc@gmail.com>
jimmyxian <jimmyxian2004@yahoo.com.cn> jimmyxian <jimmyxian2004@yahoo.com.cn>
@ -338,6 +371,7 @@ Johannes 'fish' Ziemke <github@freigeist.org>
John Feminella <jxf@jxf.me> John Feminella <jxf@jxf.me>
John Harris <john@johnharris.io> John Harris <john@johnharris.io>
John Howard <github@lowenna.com> John Howard <github@lowenna.com>
John Howard <howardjohn@google.com>
John Laswell <john.n.laswell@gmail.com> John Laswell <john.n.laswell@gmail.com>
John Maguire <jmaguire@duosecurity.com> John Maguire <jmaguire@duosecurity.com>
John Mulhausen <john@docker.com> John Mulhausen <john@docker.com>
@ -347,13 +381,16 @@ John Tims <john.k.tims@gmail.com>
John V. Martinez <jvmatl@gmail.com> John V. Martinez <jvmatl@gmail.com>
John Willis <john.willis@docker.com> John Willis <john.willis@docker.com>
Jon Johnson <jonjohnson@google.com> Jon Johnson <jonjohnson@google.com>
Jon Zeolla <zeolla@gmail.com>
Jonatas Baldin <jonatas.baldin@gmail.com> Jonatas Baldin <jonatas.baldin@gmail.com>
Jonathan Boulle <jonathanboulle@gmail.com> Jonathan Boulle <jonathanboulle@gmail.com>
Jonathan Lee <jonjohn1232009@gmail.com> Jonathan Lee <jonjohn1232009@gmail.com>
Jonathan Lomas <jonathan@floatinglomas.ca> Jonathan Lomas <jonathan@floatinglomas.ca>
Jonathan McCrohan <jmccrohan@gmail.com> Jonathan McCrohan <jmccrohan@gmail.com>
Jonathan Warriss-Simmons <misterws@diogenes.ws>
Jonh Wendell <jonh.wendell@redhat.com> Jonh Wendell <jonh.wendell@redhat.com>
Jordan Jennings <jjn2009@gmail.com> Jordan Jennings <jjn2009@gmail.com>
Jorge Vallecillo <jorgevallecilloc@gmail.com>
Jose J. Escobar <53836904+jescobar-docker@users.noreply.github.com> Jose J. Escobar <53836904+jescobar-docker@users.noreply.github.com>
Joseph Kern <jkern@semafour.net> Joseph Kern <jkern@semafour.net>
Josh Bodah <jb3689@yahoo.com> Josh Bodah <jb3689@yahoo.com>
@ -383,9 +420,11 @@ Katie McLaughlin <katie@glasnt.com>
Ke Xu <leonhartx.k@gmail.com> Ke Xu <leonhartx.k@gmail.com>
Kei Ohmura <ohmura.kei@gmail.com> Kei Ohmura <ohmura.kei@gmail.com>
Keith Hudgins <greenman@greenman.org> Keith Hudgins <greenman@greenman.org>
Kelton Bassingthwaite <KeltonBassingthwaite@gmail.com>
Ken Cochrane <kencochrane@gmail.com> Ken Cochrane <kencochrane@gmail.com>
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com> Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com> Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
Kevin Alvarez <crazy-max@users.noreply.github.com>
Kevin Burke <kev@inburke.com> Kevin Burke <kev@inburke.com>
Kevin Feyrer <kevin.feyrer@btinternet.com> Kevin Feyrer <kevin.feyrer@btinternet.com>
Kevin Kern <kaiwentan@harmonycloud.cn> Kevin Kern <kaiwentan@harmonycloud.cn>
@ -401,6 +440,7 @@ Krasi Georgiev <krasi@vip-consult.solutions>
Kris-Mikael Krister <krismikael@protonmail.com> Kris-Mikael Krister <krismikael@protonmail.com>
Kun Zhang <zkazure@gmail.com> Kun Zhang <zkazure@gmail.com>
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
Kyle Mitofsky <Kylemit@gmail.com>
Lachlan Cooper <lachlancooper@gmail.com> Lachlan Cooper <lachlancooper@gmail.com>
Lai Jiangshan <jiangshanlai@gmail.com> Lai Jiangshan <jiangshanlai@gmail.com>
Lars Kellogg-Stedman <lars@redhat.com> Lars Kellogg-Stedman <lars@redhat.com>
@ -410,6 +450,7 @@ Lee Gaines <eightlimbed@gmail.com>
Lei Jitang <leijitang@huawei.com> Lei Jitang <leijitang@huawei.com>
Lennie <github@consolejunkie.net> Lennie <github@consolejunkie.net>
Leo Gallucci <elgalu3@gmail.com> Leo Gallucci <elgalu3@gmail.com>
Leonid Skorospelov <leosko94@gmail.com>
Lewis Daly <lewisdaly@me.com> Lewis Daly <lewisdaly@me.com>
Li Yi <denverdino@gmail.com> Li Yi <denverdino@gmail.com>
Li Yi <weiyuan.yl@alibaba-inc.com> Li Yi <weiyuan.yl@alibaba-inc.com>
@ -445,6 +486,7 @@ Manjunath A Kumatagi <mkumatag@in.ibm.com>
Mansi Nahar <mmn4185@rit.edu> Mansi Nahar <mmn4185@rit.edu>
mapk0y <mapk0y@gmail.com> mapk0y <mapk0y@gmail.com>
Marc Bihlmaier <marc.bihlmaier@reddoxx.com> Marc Bihlmaier <marc.bihlmaier@reddoxx.com>
Marc Cornellà <hello@mcornella.com>
Marco Mariani <marco.mariani@alterway.fr> Marco Mariani <marco.mariani@alterway.fr>
Marco Vedovati <mvedovati@suse.com> Marco Vedovati <mvedovati@suse.com>
Marcus Martins <marcus@docker.com> Marcus Martins <marcus@docker.com>
@ -467,11 +509,13 @@ Matthieu Hauglustaine <matt.hauglustaine@gmail.com>
Mauro Porras P <mauroporrasp@gmail.com> Mauro Porras P <mauroporrasp@gmail.com>
Max Shytikov <mshytikov@gmail.com> Max Shytikov <mshytikov@gmail.com>
Maxime Petazzoni <max@signalfuse.com> Maxime Petazzoni <max@signalfuse.com>
Maximillian Fan Xavier <maximillianfx@gmail.com>
Mei ChunTao <mei.chuntao@zte.com.cn> Mei ChunTao <mei.chuntao@zte.com.cn>
Metal <2466052+tedhexaflow@users.noreply.github.com>
Micah Zoltu <micah@newrelic.com> Micah Zoltu <micah@newrelic.com>
Michael A. Smith <michael@smith-li.com> Michael A. Smith <michael@smith-li.com>
Michael Bridgen <mikeb@squaremobius.net> Michael Bridgen <mikeb@squaremobius.net>
Michael Crosby <michael@docker.com> Michael Crosby <crosbymichael@gmail.com>
Michael Friis <friism@gmail.com> Michael Friis <friism@gmail.com>
Michael Irwin <mikesir87@gmail.com> Michael Irwin <mikesir87@gmail.com>
Michael Käufl <docker@c.michael-kaeufl.de> Michael Käufl <docker@c.michael-kaeufl.de>
@ -487,6 +531,7 @@ Mihai Borobocea <MihaiBorob@gmail.com>
Mihuleacc Sergiu <mihuleac.sergiu@gmail.com> Mihuleacc Sergiu <mihuleac.sergiu@gmail.com>
Mike Brown <brownwm@us.ibm.com> Mike Brown <brownwm@us.ibm.com>
Mike Casas <mkcsas0@gmail.com> Mike Casas <mkcsas0@gmail.com>
Mike Dalton <mikedalton@github.com>
Mike Danese <mikedanese@google.com> Mike Danese <mikedanese@google.com>
Mike Dillon <mike@embody.org> Mike Dillon <mike@embody.org>
Mike Goelzer <mike.goelzer@docker.com> Mike Goelzer <mike.goelzer@docker.com>
@ -503,7 +548,9 @@ Mohini Anne Dsouza <mohini3917@gmail.com>
Moorthy RS <rsmoorthy@gmail.com> Moorthy RS <rsmoorthy@gmail.com>
Morgan Bauer <mbauer@us.ibm.com> Morgan Bauer <mbauer@us.ibm.com>
Morten Hekkvang <morten.hekkvang@sbab.se> Morten Hekkvang <morten.hekkvang@sbab.se>
Morten Linderud <morten@linderud.pw>
Moysés Borges <moysesb@gmail.com> Moysés Borges <moysesb@gmail.com>
Mozi <29089388+pzhlkj6612@users.noreply.github.com>
Mrunal Patel <mrunalp@gmail.com> Mrunal Patel <mrunalp@gmail.com>
muicoder <muicoder@gmail.com> muicoder <muicoder@gmail.com>
Muthukumar R <muthur@gmail.com> Muthukumar R <muthur@gmail.com>
@ -535,6 +582,8 @@ Noah Treuhaft <noah.treuhaft@docker.com>
O.S. Tezer <ostezer@gmail.com> O.S. Tezer <ostezer@gmail.com>
Odin Ugedal <odin@ugedal.com> Odin Ugedal <odin@ugedal.com>
ohmystack <jun.jiang02@ele.me> ohmystack <jun.jiang02@ele.me>
OKA Naoya <git@okanaoya.com>
Oliver Pomeroy <oppomeroy@gmail.com>
Olle Jonsson <olle.jonsson@gmail.com> Olle Jonsson <olle.jonsson@gmail.com>
Olli Janatuinen <olli.janatuinen@gmail.com> Olli Janatuinen <olli.janatuinen@gmail.com>
Oscar Wieman <oscrx@icloud.com> Oscar Wieman <oscrx@icloud.com>
@ -550,9 +599,12 @@ Paul Lietar <paul@lietar.net>
Paul Mulders <justinkb@gmail.com> Paul Mulders <justinkb@gmail.com>
Paul Weaver <pauweave@cisco.com> Paul Weaver <pauweave@cisco.com>
Pavel Pospisil <pospispa@gmail.com> Pavel Pospisil <pospispa@gmail.com>
Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Pokrywka <pepawel@users.noreply.github.com>
Paweł Szczekutowicz <pszczekutowicz@gmail.com> Paweł Szczekutowicz <pszczekutowicz@gmail.com>
Peeyush Gupta <gpeeyush@linux.vnet.ibm.com> Peeyush Gupta <gpeeyush@linux.vnet.ibm.com>
Per Lundberg <per.lundberg@ecraft.com> Per Lundberg <per.lundberg@ecraft.com>
Peter Dave Hello <hsu@peterdavehello.org>
Peter Edge <peter.edge@gmail.com> Peter Edge <peter.edge@gmail.com>
Peter Hsu <shhsu@microsoft.com> Peter Hsu <shhsu@microsoft.com>
Peter Jaffe <pjaffe@nevo.com> Peter Jaffe <pjaffe@nevo.com>
@ -560,11 +612,13 @@ Peter Kehl <peter.kehl@gmail.com>
Peter Nagy <xificurC@gmail.com> Peter Nagy <xificurC@gmail.com>
Peter Salvatore <peter@psftw.com> Peter Salvatore <peter@psftw.com>
Peter Waller <p@pwaller.net> Peter Waller <p@pwaller.net>
Phil Estes <estesp@linux.vnet.ibm.com> Phil Estes <estesp@gmail.com>
Philip Alexander Etling <paetling@gmail.com> Philip Alexander Etling <paetling@gmail.com>
Philipp Gillé <philipp.gille@gmail.com> Philipp Gillé <philipp.gille@gmail.com>
Philipp Schmied <pschmied@schutzwerk.com> Philipp Schmied <pschmied@schutzwerk.com>
Phong Tran <tran.pho@northeastern.edu>
pidster <pid@pidster.com> pidster <pid@pidster.com>
Pieter E Smit <diepes@github.com>
pixelistik <pixelistik@users.noreply.github.com> pixelistik <pixelistik@users.noreply.github.com>
Pratik Karki <prertik@outlook.com> Pratik Karki <prertik@outlook.com>
Prayag Verma <prayag.verma@gmail.com> Prayag Verma <prayag.verma@gmail.com>
@ -574,6 +628,7 @@ Qiang Huang <h.huangqiang@huawei.com>
Qinglan Peng <qinglanpeng@zju.edu.cn> Qinglan Peng <qinglanpeng@zju.edu.cn>
qudongfang <qudongfang@gmail.com> qudongfang <qudongfang@gmail.com>
Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com> Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com>
Rahul Kadyan <hi@znck.me>
Rahul Zoldyck <rahulzoldyck@gmail.com> Rahul Zoldyck <rahulzoldyck@gmail.com>
Ravi Shekhar Jethani <rsjethani@gmail.com> Ravi Shekhar Jethani <rsjethani@gmail.com>
Ray Tsang <rayt@google.com> Ray Tsang <rayt@google.com>
@ -582,6 +637,7 @@ Remy Suen <remy.suen@gmail.com>
Renaud Gaubert <rgaubert@nvidia.com> Renaud Gaubert <rgaubert@nvidia.com>
Ricardo N Feliciano <FelicianoTech@gmail.com> Ricardo N Feliciano <FelicianoTech@gmail.com>
Rich Moyse <rich@moyse.us> Rich Moyse <rich@moyse.us>
Richard Chen Zheng <58443436+rchenzheng@users.noreply.github.com>
Richard Mathie <richard.mathie@amey.co.uk> Richard Mathie <richard.mathie@amey.co.uk>
Richard Scothern <richard.scothern@gmail.com> Richard Scothern <richard.scothern@gmail.com>
Rick Wieman <git@rickw.nl> Rick Wieman <git@rickw.nl>
@ -591,6 +647,7 @@ Rob Gulewich <rgulewich@netflix.com>
Robert Wallis <smilingrob@gmail.com> Robert Wallis <smilingrob@gmail.com>
Robin Naundorf <r.naundorf@fh-muenster.de> Robin Naundorf <r.naundorf@fh-muenster.de>
Robin Speekenbrink <robin@kingsquare.nl> Robin Speekenbrink <robin@kingsquare.nl>
Roch Feuillade <roch.feuillade@pandobac.com>
Rodolfo Ortiz <rodolfo.ortiz@definityfirst.com> Rodolfo Ortiz <rodolfo.ortiz@definityfirst.com>
Rogelio Canedo <rcanedo@mappy.priv> Rogelio Canedo <rcanedo@mappy.priv>
Rohan Verma <hello@rohanverma.net> Rohan Verma <hello@rohanverma.net>
@ -614,6 +671,7 @@ Sambuddha Basu <sambuddhabasu1@gmail.com>
Sami Tabet <salph.tabet@gmail.com> Sami Tabet <salph.tabet@gmail.com>
Samuel Cochran <sj26@sj26.com> Samuel Cochran <sj26@sj26.com>
Samuel Karp <skarp@amazon.com> Samuel Karp <skarp@amazon.com>
Sandro Jäckel <sandro.jaeckel@gmail.com>
Santhosh Manohar <santhosh@docker.com> Santhosh Manohar <santhosh@docker.com>
Sargun Dhillon <sargun@netflix.com> Sargun Dhillon <sargun@netflix.com>
Saswat Bhattacharya <sas.saswat@gmail.com> Saswat Bhattacharya <sas.saswat@gmail.com>
@ -654,6 +712,7 @@ Stephen Rust <srust@blockbridge.com>
Steve Durrheimer <s.durrheimer@gmail.com> Steve Durrheimer <s.durrheimer@gmail.com>
Steve Richards <steve.richards@docker.com> Steve Richards <steve.richards@docker.com>
Steven Burgess <steven.a.burgess@hotmail.com> Steven Burgess <steven.a.burgess@hotmail.com>
Stoica-Marcu Floris-Andrei <floris.sm@gmail.com>
Subhajit Ghosh <isubuz.g@gmail.com> Subhajit Ghosh <isubuz.g@gmail.com>
Sun Jianbo <wonderflow.sun@gmail.com> Sun Jianbo <wonderflow.sun@gmail.com>
Sune Keller <absukl@almbrand.dk> Sune Keller <absukl@almbrand.dk>
@ -665,7 +724,10 @@ Sébastien HOUZÉ <cto@verylastroom.com>
T K Sourabh <sourabhtk37@gmail.com> T K Sourabh <sourabhtk37@gmail.com>
TAGOMORI Satoshi <tagomoris@gmail.com> TAGOMORI Satoshi <tagomoris@gmail.com>
taiji-tech <csuhqg@foxmail.com> taiji-tech <csuhqg@foxmail.com>
Takeshi Koenuma <t.koenuma2@gmail.com>
Takuya Noguchi <takninnovationresearch@gmail.com>
Taylor Jones <monitorjbl@gmail.com> Taylor Jones <monitorjbl@gmail.com>
Teiva Harsanyi <t.harsanyi@thebeat.co>
Tejaswini Duggaraju <naduggar@microsoft.com> Tejaswini Duggaraju <naduggar@microsoft.com>
Tengfei Wang <tfwang@alauda.io> Tengfei Wang <tfwang@alauda.io>
Teppei Fukuda <knqyf263@gmail.com> Teppei Fukuda <knqyf263@gmail.com>
@ -696,6 +758,7 @@ Tom Fotherby <tom+github@peopleperhour.com>
Tom Klingenberg <tklingenberg@lastflood.net> Tom Klingenberg <tklingenberg@lastflood.net>
Tom Milligan <code@tommilligan.net> Tom Milligan <code@tommilligan.net>
Tom X. Tobin <tomxtobin@tomxtobin.com> Tom X. Tobin <tomxtobin@tomxtobin.com>
Tomas Bäckman <larstomas@gmail.com>
Tomas Tomecek <ttomecek@redhat.com> Tomas Tomecek <ttomecek@redhat.com>
Tomasz Kopczynski <tomek@kopczynski.net.pl> Tomasz Kopczynski <tomek@kopczynski.net.pl>
Tomáš Hrčka <thrcka@redhat.com> Tomáš Hrčka <thrcka@redhat.com>
@ -711,6 +774,7 @@ Ulrich Bareth <ulrich.bareth@gmail.com>
Ulysses Souza <ulysses.souza@docker.com> Ulysses Souza <ulysses.souza@docker.com>
Umesh Yadav <umesh4257@gmail.com> Umesh Yadav <umesh4257@gmail.com>
Valentin Lorentz <progval+git@progval.net> Valentin Lorentz <progval+git@progval.net>
Vardan Pogosian <vardan.pogosyan@gmail.com>
Venkateswara Reddy Bukkasamudram <bukkasamudram@outlook.com> Venkateswara Reddy Bukkasamudram <bukkasamudram@outlook.com>
Veres Lajos <vlajos@gmail.com> Veres Lajos <vlajos@gmail.com>
Victor Vieux <victor.vieux@docker.com> Victor Vieux <victor.vieux@docker.com>
@ -757,6 +821,7 @@ Yunxiang Huang <hyxqshk@vip.qq.com>
Zachary Romero <zacromero3@gmail.com> Zachary Romero <zacromero3@gmail.com>
Zander Mackie <zmackie@gmail.com> Zander Mackie <zmackie@gmail.com>
zebrilee <zebrilee@gmail.com> zebrilee <zebrilee@gmail.com>
Zeel B Patel <patel_zeel@iitgn.ac.in>
Zhang Kun <zkazure@gmail.com> Zhang Kun <zkazure@gmail.com>
Zhang Wei <zhangwei555@huawei.com> Zhang Wei <zhangwei555@huawei.com>
Zhang Wentao <zhangwentao234@huawei.com> Zhang Wentao <zhangwentao234@huawei.com>
@ -768,4 +833,5 @@ Zhu Guihua <zhugh.fnst@cn.fujitsu.com>
Álex González <agonzalezro@gmail.com> Álex González <agonzalezro@gmail.com>
Álvaro Lázaro <alvaro.lazaro.g@gmail.com> Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
Átila Camurça Alves <camurca.home@gmail.com> Átila Camurça Alves <camurca.home@gmail.com>
Александр Менщиков <__Singleton__@hackerdom.ru>
徐俊杰 <paco.xu@daocloud.io> 徐俊杰 <paco.xu@daocloud.io>

@ -1,6 +1,9 @@
package manager package manager
import ( import (
"fmt"
"os"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -31,12 +34,13 @@ const (
// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid // AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid
// plugin. The command stubs will have several annotations added, see // plugin. The command stubs will have several annotations added, see
// `CommandAnnotationPlugin*`. // `CommandAnnotationPlugin*`.
func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command) error { func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) error {
plugins, err := ListPlugins(dockerCli, cmd) plugins, err := ListPlugins(dockerCli, rootCmd)
if err != nil { if err != nil {
return err return err
} }
for _, p := range plugins { for _, p := range plugins {
p := p
vendor := p.Vendor vendor := p.Vendor
if vendor == "" { if vendor == "" {
vendor = "unknown" vendor = "unknown"
@ -49,11 +53,41 @@ func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command) error {
if p.Err != nil { if p.Err != nil {
annotations[CommandAnnotationPluginInvalid] = p.Err.Error() annotations[CommandAnnotationPluginInvalid] = p.Err.Error()
} }
cmd.AddCommand(&cobra.Command{ rootCmd.AddCommand(&cobra.Command{
Use: p.Name, Use: p.Name,
Short: p.ShortDescription, Short: p.ShortDescription,
Run: func(_ *cobra.Command, _ []string) {}, Run: func(_ *cobra.Command, _ []string) {},
Annotations: annotations, Annotations: annotations,
DisableFlagParsing: true,
RunE: func(cmd *cobra.Command, args []string) error {
flags := rootCmd.PersistentFlags()
flags.SetOutput(nil)
err := flags.Parse(args)
if err != nil {
return err
}
if flags.Changed("help") {
cmd.HelpFunc()(rootCmd, args)
return nil
}
return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'", cmd.Name())
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Delegate completion to plugin
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
cargs = append(cargs, args...)
cargs = append(cargs, toComplete)
os.Args = cargs
runCommand, err := PluginRunCommand(dockerCli, p.Name, cmd)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
err = runCommand.Run()
if err == nil {
os.Exit(0) // plugin already rendered complete data
}
return nil, cobra.ShellCompDirectiveError
},
}) })
} }
return nil return nil

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package manager package manager

@ -34,7 +34,7 @@ type Plugin struct {
// returned with no error. An error is only returned due to a // returned with no error. An error is only returned due to a
// non-recoverable error. // non-recoverable error.
// //
// nolint: gocyclo //nolint:gocyclo
func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
path := c.Path() path := c.Path()
if path == "" { if path == "" {

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package manager package manager

@ -125,6 +125,11 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
}, },
TraverseChildren: true, TraverseChildren: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: false,
HiddenDefaultCmd: true,
DisableDescriptions: true,
},
} }
opts, flags := cli.SetupPluginRootCommand(cmd) opts, flags := cli.SetupPluginRootCommand(cmd)

@ -3,12 +3,17 @@ package cli
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"sort"
"strings" "strings"
pluginmanager "github.com/docker/cli/cli-plugins/manager" pluginmanager "github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
cliflags "github.com/docker/cli/cli/flags" cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/docker/pkg/homedir"
"github.com/docker/docker/registry"
"github.com/fvbommel/sortorder"
"github.com/moby/term" "github.com/moby/term"
"github.com/morikuni/aec" "github.com/morikuni/aec"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -22,15 +27,21 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p
opts := cliflags.NewClientOptions() opts := cliflags.NewClientOptions()
flags := rootCmd.Flags() flags := rootCmd.Flags()
flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files") flags.StringVar(&opts.ConfigDir, "config", config.Dir(), "Location of client config files")
opts.Common.InstallFlags(flags) opts.Common.InstallFlags(flags)
cobra.AddTemplateFunc("add", func(a, b int) int { return a + b }) cobra.AddTemplateFunc("add", func(a, b int) int { return a + b })
cobra.AddTemplateFunc("hasAliases", hasAliases)
cobra.AddTemplateFunc("hasSubCommands", hasSubCommands) cobra.AddTemplateFunc("hasSubCommands", hasSubCommands)
cobra.AddTemplateFunc("hasTopCommands", hasTopCommands)
cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands) cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands)
cobra.AddTemplateFunc("hasSwarmSubCommands", hasSwarmSubCommands)
cobra.AddTemplateFunc("hasInvalidPlugins", hasInvalidPlugins) cobra.AddTemplateFunc("hasInvalidPlugins", hasInvalidPlugins)
cobra.AddTemplateFunc("topCommands", topCommands)
cobra.AddTemplateFunc("commandAliases", commandAliases)
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands) cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands) cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
cobra.AddTemplateFunc("orchestratorSubCommands", orchestratorSubCommands)
cobra.AddTemplateFunc("invalidPlugins", invalidPlugins) cobra.AddTemplateFunc("invalidPlugins", invalidPlugins)
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages) cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
cobra.AddTemplateFunc("vendorAndVersion", vendorAndVersion) cobra.AddTemplateFunc("vendorAndVersion", vendorAndVersion)
@ -52,6 +63,13 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p
rootCmd.Annotations = map[string]string{"additionalHelp": "To get more help with docker, check out our guides at https://docs.docker.com/go/guides/"} rootCmd.Annotations = map[string]string{"additionalHelp": "To get more help with docker, check out our guides at https://docs.docker.com/go/guides/"}
// Configure registry.CertsDir() when running in rootless-mode
if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
if configHome, err := homedir.GetConfigHome(); err == nil {
registry.SetCertsDir(filepath.Join(configHome, "docker/certs.d"))
}
}
return opts, flags, helpCommand return opts, flags, helpCommand
} }
@ -222,6 +240,10 @@ func isPlugin(cmd *cobra.Command) bool {
return cmd.Annotations[pluginmanager.CommandAnnotationPlugin] == "true" return cmd.Annotations[pluginmanager.CommandAnnotationPlugin] == "true"
} }
func hasAliases(cmd *cobra.Command) bool {
return len(cmd.Aliases) > 0 || cmd.Annotations["aliases"] != ""
}
func hasSubCommands(cmd *cobra.Command) bool { func hasSubCommands(cmd *cobra.Command) bool {
return len(operationSubCommands(cmd)) > 0 return len(operationSubCommands(cmd)) > 0
} }
@ -230,16 +252,69 @@ func hasManagementSubCommands(cmd *cobra.Command) bool {
return len(managementSubCommands(cmd)) > 0 return len(managementSubCommands(cmd)) > 0
} }
func hasSwarmSubCommands(cmd *cobra.Command) bool {
return len(orchestratorSubCommands(cmd)) > 0
}
func hasInvalidPlugins(cmd *cobra.Command) bool { func hasInvalidPlugins(cmd *cobra.Command) bool {
return len(invalidPlugins(cmd)) > 0 return len(invalidPlugins(cmd)) > 0
} }
func hasTopCommands(cmd *cobra.Command) bool {
return len(topCommands(cmd)) > 0
}
// commandAliases is a templating function to return aliases for the command,
// formatted as the full command as they're called (contrary to the default
// Aliases function, which only returns the subcommand).
func commandAliases(cmd *cobra.Command) string {
if cmd.Annotations["aliases"] != "" {
return cmd.Annotations["aliases"]
}
var parentPath string
if cmd.HasParent() {
parentPath = cmd.Parent().CommandPath() + " "
}
aliases := cmd.CommandPath()
for _, alias := range cmd.Aliases {
aliases += ", " + parentPath + alias
}
return aliases
}
func topCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{}
if cmd.Parent() != nil {
// for now, only use top-commands for the root-command, and skip
// for sub-commands
return cmds
}
for _, sub := range cmd.Commands() {
if isPlugin(sub) || !sub.IsAvailableCommand() {
continue
}
if _, ok := sub.Annotations["category-top"]; ok {
cmds = append(cmds, sub)
}
}
sort.SliceStable(cmds, func(i, j int) bool {
return sortorder.NaturalLess(cmds[i].Annotations["category-top"], cmds[j].Annotations["category-top"])
})
return cmds
}
func operationSubCommands(cmd *cobra.Command) []*cobra.Command { func operationSubCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{} cmds := []*cobra.Command{}
for _, sub := range cmd.Commands() { for _, sub := range cmd.Commands() {
if isPlugin(sub) { if isPlugin(sub) {
continue continue
} }
if _, ok := sub.Annotations["category-top"]; ok {
if cmd.Parent() == nil {
// for now, only use top-commands for the root-command
continue
}
}
if sub.IsAvailableCommand() && !sub.HasSubCommands() { if sub.IsAvailableCommand() && !sub.HasSubCommands() {
cmds = append(cmds, sub) cmds = append(cmds, sub)
} }
@ -275,6 +350,27 @@ func vendorAndVersion(cmd *cobra.Command) string {
} }
func managementSubCommands(cmd *cobra.Command) []*cobra.Command { func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{}
for _, sub := range allManagementSubCommands(cmd) {
if _, ok := sub.Annotations["swarm"]; ok {
continue
}
cmds = append(cmds, sub)
}
return cmds
}
func orchestratorSubCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{}
for _, sub := range allManagementSubCommands(cmd) {
if _, ok := sub.Annotations["swarm"]; ok {
cmds = append(cmds, sub)
}
}
return cmds
}
func allManagementSubCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{} cmds := []*cobra.Command{}
for _, sub := range cmd.Commands() { for _, sub := range cmd.Commands() {
if isPlugin(sub) { if isPlugin(sub) {
@ -323,10 +419,10 @@ EXPERIMENTAL:
https://docs.docker.com/go/experimental/ https://docs.docker.com/go/experimental/
{{- end}} {{- end}}
{{- if gt .Aliases 0}} {{- if hasAliases . }}
Aliases: Aliases:
{{.NameAndAliases}} {{ commandAliases . }}
{{- end}} {{- end}}
{{- if .HasExample}} {{- if .HasExample}}
@ -335,11 +431,20 @@ Examples:
{{ .Example }} {{ .Example }}
{{- end}} {{- end}}
{{- if .HasParent}}
{{- if .HasAvailableFlags}} {{- if .HasAvailableFlags}}
Options: Options:
{{ wrappedFlagUsages . | trimRightSpace}} {{ wrappedFlagUsages . | trimRightSpace}}
{{- end}}
{{- end}}
{{- if hasTopCommands .}}
Common Commands:
{{- range topCommands .}}
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}
{{- end}}
{{- end}} {{- end}}
{{- if hasManagementSubCommands . }} {{- if hasManagementSubCommands . }}
@ -349,6 +454,15 @@ Management Commands:
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}} {{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}}
{{- end}} {{- end}}
{{- end}}
{{- if hasSwarmSubCommands . }}
Swarm Commands:
{{- range orchestratorSubCommands . }}
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}}
{{- end}}
{{- end}} {{- end}}
{{- if hasSubCommands .}} {{- if hasSubCommands .}}
@ -367,6 +481,14 @@ Invalid Plugins:
{{rpad .Name .NamePadding }} {{invalidPluginReason .}} {{rpad .Name .NamePadding }} {{invalidPluginReason .}}
{{- end}} {{- end}}
{{- end}}
{{- if not .HasParent}}
{{- if .HasAvailableFlags}}
Global Options:
{{ wrappedFlagUsages . | trimRightSpace}}
{{- end}}
{{- end}} {{- end}}
{{- if .HasSubCommands }} {{- if .HasSubCommands }}

@ -11,7 +11,6 @@ import (
"time" "time"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
cliconfig "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/configfile"
dcontext "github.com/docker/cli/cli/context" dcontext "github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/docker"
@ -26,7 +25,8 @@ import (
dopts "github.com/docker/cli/opts" dopts "github.com/docker/cli/opts"
"github.com/docker/docker/api" "github.com/docker/docker/api"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
"github.com/moby/term" "github.com/moby/term"
@ -52,7 +52,6 @@ type Cli interface {
Apply(ops ...DockerCliOption) error Apply(ops ...DockerCliOption) error
ConfigFile() *configfile.ConfigFile ConfigFile() *configfile.ConfigFile
ServerInfo() ServerInfo ServerInfo() ServerInfo
ClientInfo() ClientInfo
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
DefaultVersion() string DefaultVersion() string
ManifestStore() manifeststore.Store ManifestStore() manifeststore.Store
@ -73,7 +72,6 @@ type DockerCli struct {
err io.Writer err io.Writer
client client.APIClient client client.APIClient
serverInfo ServerInfo serverInfo ServerInfo
clientInfo *ClientInfo
contentTrust bool contentTrust bool
contextStore store.Store contextStore store.Store
currentContext string currentContext string
@ -81,9 +79,9 @@ type DockerCli struct {
contextStoreConfig store.Config contextStoreConfig store.Config
} }
// DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified. // DefaultVersion returns api.defaultVersion.
func (cli *DockerCli) DefaultVersion() string { func (cli *DockerCli) DefaultVersion() string {
return cli.ClientInfo().DefaultVersion return api.DefaultVersion
} }
// Client returns the APIClient // Client returns the APIClient
@ -129,39 +127,16 @@ func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
} }
func (cli *DockerCli) loadConfigFile() { func (cli *DockerCli) loadConfigFile() {
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err) cli.configFile = config.LoadDefaultConfigFile(cli.err)
} }
// ServerInfo returns the server version details for the host this client is // ServerInfo returns the server version details for the host this client is
// connected to // connected to
func (cli *DockerCli) ServerInfo() ServerInfo { func (cli *DockerCli) ServerInfo() ServerInfo {
// TODO(thaJeztah) make ServerInfo() lazily load the info (ping only when needed)
return cli.serverInfo return cli.serverInfo
} }
// ClientInfo returns the client details for the cli
func (cli *DockerCli) ClientInfo() ClientInfo {
if cli.clientInfo == nil {
if err := cli.loadClientInfo(); err != nil {
panic(err)
}
}
return *cli.clientInfo
}
func (cli *DockerCli) loadClientInfo() error {
var v string
if cli.client != nil {
v = cli.client.ClientVersion()
} else {
v = api.DefaultVersion
}
cli.clientInfo = &ClientInfo{
DefaultVersion: v,
HasExperimental: true,
}
return nil
}
// ContentTrustEnabled returns whether content trust has been enabled by an // ContentTrustEnabled returns whether content trust has been enabled by an
// environment variable. // environment variable.
func (cli *DockerCli) ContentTrustEnabled() bool { func (cli *DockerCli) ContentTrustEnabled() bool {
@ -197,7 +172,7 @@ func (cli *DockerCli) ManifestStore() manifeststore.Store {
// RegistryClient returns a client for communicating with a Docker distribution // RegistryClient returns a client for communicating with a Docker distribution
// registry // registry
func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient { func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient {
resolver := func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig { resolver := func(ctx context.Context, index *registry.IndexInfo) types.AuthConfig {
return ResolveAuthConfig(ctx, cli, index) return ResolveAuthConfig(ctx, cli, index)
} }
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure) return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
@ -228,7 +203,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
cliflags.SetLogLevel(opts.Common.LogLevel) cliflags.SetLogLevel(opts.Common.LogLevel)
if opts.ConfigDir != "" { if opts.ConfigDir != "" {
cliconfig.SetDir(opts.ConfigDir) config.SetDir(opts.ConfigDir)
} }
if opts.Common.Debug { if opts.Common.Debug {
@ -237,7 +212,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
cli.loadConfigFile() cli.loadConfigFile()
baseContextStore := store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig) baseContextStore := store.New(config.ContextStoreDir(), cli.contextStoreConfig)
cli.contextStore = &ContextStoreWithDefault{ cli.contextStore = &ContextStoreWithDefault{
Store: baseContextStore, Store: baseContextStore,
Resolver: func() (*DefaultContext, error) { Resolver: func() (*DefaultContext, error) {
@ -260,28 +235,23 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
} }
} }
cli.initializeFromClient() cli.initializeFromClient()
if err := cli.loadClientInfo(); err != nil {
return err
}
return nil return nil
} }
// NewAPIClientFromFlags creates a new APIClient from command line flags // NewAPIClientFromFlags creates a new APIClient from command line flags
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
storeConfig := DefaultContextStoreConfig() storeConfig := DefaultContextStoreConfig()
store := &ContextStoreWithDefault{ contextStore := &ContextStoreWithDefault{
Store: store.New(cliconfig.ContextStoreDir(), storeConfig), Store: store.New(config.ContextStoreDir(), storeConfig),
Resolver: func() (*DefaultContext, error) { Resolver: func() (*DefaultContext, error) {
return ResolveDefaultContext(opts, configFile, storeConfig, io.Discard) return ResolveDefaultContext(opts, configFile, storeConfig, io.Discard)
}, },
} }
contextName, err := resolveContextName(opts, configFile, store) contextName, err := resolveContextName(opts, configFile, contextStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
endpoint, err := resolveDockerEndpoint(store, contextName) endpoint, err := resolveDockerEndpoint(contextStore, contextName)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to resolve docker endpoint") return nil, errors.Wrap(err, "unable to resolve docker endpoint")
} }
@ -368,6 +338,7 @@ func (cli *DockerCli) initializeFromClient() {
HasExperimental: ping.Experimental, HasExperimental: ping.Experimental,
OSType: ping.OSType, OSType: ping.OSType,
BuildkitVersion: ping.BuilderVersion, BuildkitVersion: ping.BuilderVersion,
SwarmStatus: ping.SwarmStatus,
} }
cli.client.NegotiateAPIVersionPing(ping) cli.client.NegotiateAPIVersionPing(ping)
} }
@ -408,26 +379,28 @@ type ServerInfo struct {
HasExperimental bool HasExperimental bool
OSType string OSType string
BuildkitVersion types.BuilderVersion BuildkitVersion types.BuilderVersion
}
// ClientInfo stores details about the supported features of the client // SwarmStatus provides information about the current swarm status of the
type ClientInfo struct { // engine, obtained from the "Swarm" header in the API response.
// Deprecated: experimental CLI features always enabled. This field is kept //
// for backward-compatibility, and is always "true". // It can be a nil struct if the API version does not provide this header
HasExperimental bool // in the ping response, or if an error occurred, in which case the client
DefaultVersion string // should use other ways to get the current swarm status, such as the /swarm
// endpoint.
SwarmStatus *swarm.Status
} }
// NewDockerCli returns a DockerCli instance with all operators applied on it. // NewDockerCli returns a DockerCli instance with all operators applied on it.
// It applies by default the standard streams, and the content trust from // It applies by default the standard streams, and the content trust from
// environment. // environment.
func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) { func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
cli := &DockerCli{}
defaultOps := []DockerCliOption{ defaultOps := []DockerCliOption{
WithContentTrustFromEnv(), WithContentTrustFromEnv(),
WithDefaultContextStoreConfig(),
} }
cli.contextStoreConfig = DefaultContextStoreConfig()
ops = append(defaultOps, ops...) ops = append(defaultOps, ops...)
cli := &DockerCli{}
if err := cli.Apply(ops...); err != nil { if err := cli.Apply(ops...); err != nil {
return nil, err return nil, err
} }
@ -450,7 +423,7 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error
var host string var host string
switch len(hosts) { switch len(hosts) {
case 0: case 0:
host = os.Getenv("DOCKER_HOST") host = os.Getenv(client.EnvOverrideHost)
case 1: case 1:
host = hosts[0] host = hosts[0]
default: default:
@ -468,7 +441,7 @@ func UserAgent() string {
// resolveContextName resolves the current context name with the following rules: // resolveContextName resolves the current context name with the following rules:
// - setting both --context and --host flags is ambiguous // - setting both --context and --host flags is ambiguous
// - if --context is set, use this value // - if --context is set, use this value
// - if --host flag or DOCKER_HOST is set, fallbacks to use the same logic as before context-store was added // - if --host flag or DOCKER_HOST (client.EnvOverrideHost) is set, fallbacks to use the same logic as before context-store was added
// for backward compatibility with existing scripts // for backward compatibility with existing scripts
// - if DOCKER_CONTEXT is set, use this value // - if DOCKER_CONTEXT is set, use this value
// - if Config file has a globally set "CurrentContext", use this value // - if Config file has a globally set "CurrentContext", use this value
@ -483,7 +456,7 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
if len(opts.Hosts) > 0 { if len(opts.Hosts) > 0 {
return DefaultContextName, nil return DefaultContextName, nil
} }
if _, present := os.LookupEnv("DOCKER_HOST"); present { if _, present := os.LookupEnv(client.EnvOverrideHost); present {
return DefaultContextName, nil return DefaultContextName, nil
} }
if ctxName, ok := os.LookupEnv("DOCKER_CONTEXT"); ok { if ctxName, ok := os.LookupEnv("DOCKER_CONTEXT"); ok {

@ -94,3 +94,11 @@ func WithContextEndpointType(endpointName string, endpointType store.TypeGetter)
return nil return nil
} }
} }
// WithDefaultContextStoreConfig configures the cli to use the default context store configuration.
func WithDefaultContextStoreConfig() DockerCliOption {
return func(cli *DockerCli) error {
cli.contextStoreConfig = DefaultContextStoreConfig()
return nil
}
}

@ -12,7 +12,6 @@ import (
"strings" "strings"
configtypes "github.com/docker/cli/cli/config/types" configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/debug"
"github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/streams"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@ -22,28 +21,10 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// ElectAuthServer returns the default registry to use (by asking the daemon) // ElectAuthServer returns the default registry to use
func ElectAuthServer(ctx context.Context, cli Cli) string { // Deprecated: use registry.IndexServer instead
// The daemon `/info` endpoint informs us of the default registry being func ElectAuthServer(_ context.Context, _ Cli) string {
// used. This is essential in cross-platforms environment, where for return registry.IndexServer
// example a Linux client might be interacting with a Windows daemon, hence
// the default registry URL might be Windows specific.
info, err := cli.Client().Info(ctx)
if err != nil {
// Daemon is not responding so use system default.
if debug.IsEnabled() {
// Only report the warning if we're in debug mode to prevent nagging during engine initialization workflows
fmt.Fprintf(cli.Err(), "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, registry.IndexServer)
}
return registry.IndexServer
}
if info.IndexServerAddress == "" {
if debug.IsEnabled() {
fmt.Fprintf(cli.Err(), "Warning: Empty registry endpoint from daemon. Using system default: %s\n", registry.IndexServer)
}
return registry.IndexServer
}
return info.IndexServerAddress
} }
// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload // EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
@ -61,7 +42,7 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
return func() (string, error) { return func() (string, error) {
fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName) fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName)
indexServer := registry.GetAuthConfigKey(index) indexServer := registry.GetAuthConfigKey(index)
isDefaultRegistry := indexServer == ElectAuthServer(context.Background(), cli) isDefaultRegistry := indexServer == registry.IndexServer
authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry) authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry)
if err != nil { if err != nil {
fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err) fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
@ -77,10 +58,10 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
// ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the // ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
// default index, it uses the default index name for the daemon's platform, // default index, it uses the default index name for the daemon's platform,
// not the client's platform. // not the client's platform.
func ResolveAuthConfig(ctx context.Context, cli Cli, index *registrytypes.IndexInfo) types.AuthConfig { func ResolveAuthConfig(_ context.Context, cli Cli, index *registrytypes.IndexInfo) types.AuthConfig {
configKey := index.Name configKey := index.Name
if index.Official { if index.Official {
configKey = ElectAuthServer(ctx, cli) configKey = registry.IndexServer
} }
a, _ := cli.ConfigFile().GetAuthConfig(configKey) a, _ := cli.ConfigFile().GetAuthConfig(configKey)

@ -19,7 +19,7 @@ import (
// CopyToFile writes the content of the reader to the specified file // CopyToFile writes the content of the reader to the specified file
func CopyToFile(outfile string, r io.Reader) error { func CopyToFile(outfile string, r io.Reader) error {
// We use sequential file access here to avoid depleting the standby list // We use sequential file access here to avoid depleting the standby list
// on Windows. On Linux, this is a call directly to ioutil.TempFile // on Windows. On Linux, this is a call directly to os.CreateTemp
tmpFile, err := system.TempFileSequential(filepath.Dir(outfile), ".docker_temp_") tmpFile, err := system.TempFileSequential(filepath.Dir(outfile), ".docker_temp_")
if err != nil { if err != nil {
return err return err

@ -19,7 +19,7 @@ const (
// ConfigFileName is the name of config file // ConfigFileName is the name of config file
ConfigFileName = "config.json" ConfigFileName = "config.json"
configFileDir = ".docker" configFileDir = ".docker"
oldConfigfile = ".dockercfg" oldConfigfile = ".dockercfg" // Deprecated: remove once we stop printing deprecation warning
contextsDir = "contexts" contextsDir = "contexts"
) )
@ -84,16 +84,6 @@ func Path(p ...string) (string, error) {
return path, nil return path, nil
} }
// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
// a non-nested reader
func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
configFile := configfile.ConfigFile{
AuthConfigs: make(map[string]types.AuthConfig),
}
err := configFile.LegacyLoadFromReader(configData)
return &configFile, err
}
// LoadFromReader is a convenience function that creates a ConfigFile object from // LoadFromReader is a convenience function that creates a ConfigFile object from
// a reader // a reader
func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
@ -140,12 +130,8 @@ func load(configDir string) (*configfile.ConfigFile, bool, error) {
// Can't find latest config file so check for the old one // Can't find latest config file so check for the old one
filename = filepath.Join(getHomeDir(), oldConfigfile) filename = filepath.Join(getHomeDir(), oldConfigfile)
if file, err := os.Open(filename); err == nil { if _, err := os.Stat(filename); err == nil {
printLegacyFileWarning = true printLegacyFileWarning = true
defer file.Close()
if err := configFile.LegacyLoadFromReader(file); err != nil {
return configFile, printLegacyFileWarning, errors.Wrap(err, filename)
}
} }
return configFile, printLegacyFileWarning, nil return configFile, printLegacyFileWarning, nil
} }
@ -158,7 +144,7 @@ func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err) fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
} }
if printLegacyFileWarning { if printLegacyFileWarning {
_, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release") _, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format has been removed and the configuration file will be ignored")
} }
if !configFile.ContainsAuth() { if !configFile.ContainsAuth() {
configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore) configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)

@ -14,13 +14,6 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const (
// This constant is only used for really old config files when the
// URL wasn't saved as part of the config file and it was just
// assumed to be this value.
defaultIndexServer = "https://index.docker.io/v1/"
)
// ConfigFile ~/.docker/config.json file info // ConfigFile ~/.docker/config.json file info
type ConfigFile struct { type ConfigFile struct {
AuthConfigs map[string]types.AuthConfig `json:"auths"` AuthConfigs map[string]types.AuthConfig `json:"auths"`
@ -71,44 +64,6 @@ func New(fn string) *ConfigFile {
} }
} }
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
// auth config information with given directory and populates the receiver object
func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
b, err := io.ReadAll(configData)
if err != nil {
return err
}
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
arr := strings.Split(string(b), "\n")
if len(arr) < 2 {
return errors.Errorf("The Auth config file is empty")
}
authConfig := types.AuthConfig{}
origAuth := strings.Split(arr[0], " = ")
if len(origAuth) != 2 {
return errors.Errorf("Invalid Auth config file")
}
authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
if err != nil {
return err
}
authConfig.ServerAddress = defaultIndexServer
configFile.AuthConfigs[defaultIndexServer] = authConfig
} else {
for k, authConfig := range configFile.AuthConfigs {
authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
if err != nil {
return err
}
authConfig.Auth = ""
authConfig.ServerAddress = k
configFile.AuthConfigs[k] = authConfig
}
}
return nil
}
// LoadFromReader reads the configuration data given and sets up the auth config // LoadFromReader reads the configuration data given and sets up the auth config
// information with given directory and populates the receiver object // information with given directory and populates the receiver object
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error { func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package configfile package configfile

@ -1,3 +1,4 @@
//go:build !windows && !darwin && !linux
// +build !windows,!darwin,!linux // +build !windows,!darwin,!linux
package credentials package credentials

@ -7,7 +7,7 @@ import (
) )
const ( const (
remoteCredentialsPrefix = "docker-credential-" remoteCredentialsPrefix = "docker-credential-" //nolint:gosec // ignore G101: Potential hardcoded credentials
tokenUsername = "<token>" tokenUsername = "<token>"
) )

@ -4,13 +4,13 @@
// For example, to provide an http.Client that can connect to a Docker daemon // For example, to provide an http.Client that can connect to a Docker daemon
// running in a Docker container ("DIND"): // running in a Docker container ("DIND"):
// //
// httpClient := &http.Client{ // httpClient := &http.Client{
// Transport: &http.Transport{ // Transport: &http.Transport{
// DialContext: func(ctx context.Context, _network, _addr string) (net.Conn, error) { // DialContext: func(ctx context.Context, _network, _addr string) (net.Conn, error) {
// return commandconn.New(ctx, "docker", "exec", "-it", containerID, "docker", "system", "dial-stdio") // return commandconn.New(ctx, "docker", "exec", "-it", containerID, "docker", "system", "dial-stdio")
// }, // },
// }, // },
// } // }
package commandconn package commandconn
import ( import (

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package commandconn package commandconn

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package commandconn package commandconn

@ -6,7 +6,6 @@ import (
"encoding/pem" "encoding/pem"
"net" "net"
"net/http" "net/http"
"os"
"time" "time"
"github.com/docker/cli/cli/connhelper" "github.com/docker/cli/cli/connhelper"
@ -68,7 +67,7 @@ func (c *Endpoint) tlsConfig() (*tls.Config, error) {
if pemBlock == nil { if pemBlock == nil {
return nil, errors.New("no valid private key found") return nil, errors.New("no valid private key found")
} }
if x509.IsEncryptedPEMBlock(pemBlock) { //nolint: staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design if x509.IsEncryptedPEMBlock(pemBlock) { //nolint:staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
return nil, errors.New("private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/") return nil, errors.New("private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/")
} }
@ -122,12 +121,7 @@ func (c *Endpoint) ClientOpts() ([]client.Opt, error) {
} }
} }
version := os.Getenv("DOCKER_API_VERSION") result = append(result, client.WithVersionFromEnv(), client.WithAPIVersionNegotiation())
if version != "" {
result = append(result, client.WithVersion(version))
} else {
result = append(result, client.WithAPIVersionNegotiation())
}
return result, nil return result, nil
} }

@ -1,20 +1,32 @@
// Package store provides a generic way to store credentials to connect to virtually any kind of remote system. // Package store provides a generic way to store credentials to connect to
// The term `context` comes from the similar feature in Kubernetes kubectl config files. // virtually any kind of remote system.
// The term `context` comes from the similar feature in Kubernetes kubectl
// config files.
// //
// Conceptually, a context is a set of metadata and TLS data, that can be used to connect to various endpoints // Conceptually, a context is a set of metadata and TLS data, that can be used
// of a remote system. TLS data and metadata are stored separately, so that in the future, we will be able to store sensitive // to connect to various endpoints of a remote system. TLS data and metadata
// information in a more secure way, depending on the os we are running on (e.g.: on Windows we could use the user Certificate Store, on Mac OS the user Keychain...). // are stored separately, so that in the future, we will be able to store
// sensitive information in a more secure way, depending on the os we are running
// on (e.g.: on Windows we could use the user Certificate Store, on macOS the
// user Keychain...).
// //
// Current implementation is purely file based with the following structure: // Current implementation is purely file based with the following structure:
// ${CONTEXT_ROOT}
// - meta/
// - <context id>/meta.json: contains context medata (key/value pairs) as well as a list of endpoints (themselves containing key/value pair metadata)
// - tls/
// - <context id>/endpoint1/: directory containing TLS data for the endpoint1 in the corresponding context
// //
// The context store itself has absolutely no knowledge about what a docker endpoint should contain in term of metadata or TLS config. // ${CONTEXT_ROOT}
// Client code is responsible for generating and parsing endpoint metadata and TLS files. // meta/
// The multi-endpoints approach of this package allows to combine many different endpoints in the same "context". // <context id>/meta.json: contains context medata (key/value pairs) as
// well as a list of endpoints (themselves containing
// key/value pair metadata).
// tls/
// <context id>/endpoint1/: directory containing TLS data for the endpoint1
// in the corresponding context.
// //
// Context IDs are actually SHA256 hashes of the context name, and are there only to avoid dealing with special characters in context names. // The context store itself has absolutely no knowledge about what a docker
// endpoint should contain in term of metadata or TLS config. Client code is
// responsible for generating and parsing endpoint metadata and TLS files. The
// multi-endpoints approach of this package allows to combine many different
// endpoints in the same "context".
//
// Context IDs are actually SHA256 hashes of the context name, and are there
// only to avoid dealing with special characters in context names.
package store package store

@ -16,7 +16,7 @@ import (
"strings" "strings"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
digest "github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -118,6 +118,19 @@ func (s *store) List() ([]Metadata, error) {
return s.meta.list() return s.meta.list()
} }
// Names return Metadata names for a Lister
func Names(s Lister) ([]string, error) {
list, err := s.List()
if err != nil {
return nil, err
}
var names []string
for _, item := range list {
names = append(names, item.Name)
}
return names, nil
}
func (s *store) CreateOrUpdate(meta Metadata) error { func (s *store) CreateOrUpdate(meta Metadata) error {
return s.meta.createOrUpdate(meta) return s.meta.createOrUpdate(meta)
} }

@ -43,7 +43,7 @@ func (s *tlsStore) getData(contextID contextdir, endpointName, filename string)
return data, nil return data, nil
} }
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error { // nolint:unused func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error { //nolint:unused
err := os.Remove(s.filePath(contextID, endpointName, filename)) err := os.Remove(s.filePath(contextID, endpointName, filename))
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return nil

@ -5,8 +5,9 @@ import (
"os" "os"
"path/filepath" "path/filepath"
cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
"github.com/docker/cli/opts" "github.com/docker/cli/opts"
"github.com/docker/docker/client"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -21,12 +22,25 @@ const (
DefaultCertFile = "cert.pem" DefaultCertFile = "cert.pem"
// FlagTLSVerify is the flag name for the TLS verification option // FlagTLSVerify is the flag name for the TLS verification option
FlagTLSVerify = "tlsverify" FlagTLSVerify = "tlsverify"
// FormatHelp describes the --format flag behavior for list commands
FormatHelp = `Format output using a custom template:
'table': Print output in table format with column headers (default)
'table TEMPLATE': Print output in table format using the given Go template
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates`
// InspectFormatHelp describes the --format flag behavior for inspect commands
InspectFormatHelp = `Format output using a custom template:
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates`
) )
var ( var (
dockerCertPath = os.Getenv("DOCKER_CERT_PATH") dockerCertPath = os.Getenv(client.EnvOverrideCertPath)
dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != "" dockerTLSVerify = os.Getenv(client.EnvTLSVerify) != ""
dockerTLS = os.Getenv("DOCKER_TLS") != "" // TODO(thaJeztah) the 'DOCKER_TLS' environment variable is not documented, and does not have a const.
dockerTLS = os.Getenv("DOCKER_TLS") != ""
) )
// CommonOptions are options common to both the client and the daemon. // CommonOptions are options common to both the client and the daemon.
@ -48,7 +62,7 @@ func NewCommonOptions() *CommonOptions {
// InstallFlags adds flags for the common options on the FlagSet // InstallFlags adds flags for the common options on the FlagSet
func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) { func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
if dockerCertPath == "" { if dockerCertPath == "" {
dockerCertPath = cliconfig.Dir() dockerCertPath = config.Dir()
} }
flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode") flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode")
@ -72,7 +86,7 @@ func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, nil) hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, nil)
flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to") flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
flags.StringVarP(&commonOpts.Context, "context", "c", "", flags.StringVarP(&commonOpts.Context, "context", "c", "",
`Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")`) `Name of the context to use to connect to the daemon (overrides `+client.EnvOverrideHost+` env var and default context set with "docker context use")`)
} }
// SetDefaultOptions sets default values for options after flag parsing is // SetDefaultOptions sets default values for options after flag parsing is

@ -10,7 +10,7 @@ import (
"github.com/docker/cli/cli/manifest/types" "github.com/docker/cli/cli/manifest/types"
"github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
digest "github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
) )

@ -7,7 +7,6 @@ import (
"strings" "strings"
manifesttypes "github.com/docker/cli/cli/manifest/types" manifesttypes "github.com/docker/cli/cli/manifest/types"
"github.com/docker/cli/cli/trust"
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
distributionclient "github.com/docker/distribution/registry/client" distributionclient "github.com/docker/distribution/registry/client"
@ -25,7 +24,6 @@ type RegistryClient interface {
GetManifestList(ctx context.Context, ref reference.Named) ([]manifesttypes.ImageManifest, error) GetManifestList(ctx context.Context, ref reference.Named) ([]manifesttypes.ImageManifest, error)
MountBlob(ctx context.Context, source reference.Canonical, target reference.Named) error MountBlob(ctx context.Context, source reference.Canonical, target reference.Named) error
PutManifest(ctx context.Context, ref reference.Named, manifest distribution.Manifest) (digest.Digest, error) PutManifest(ctx context.Context, ref reference.Named, manifest distribution.Manifest) (digest.Digest, error)
GetTags(ctx context.Context, ref reference.Named) ([]string, error)
} }
// NewRegistryClient returns a new RegistryClient with a resolver // NewRegistryClient returns a new RegistryClient with a resolver
@ -124,19 +122,6 @@ func (c *client) PutManifest(ctx context.Context, ref reference.Named, manifest
return dgst, errors.Wrapf(err, "failed to put manifest %s", ref) return dgst, errors.Wrapf(err, "failed to put manifest %s", ref)
} }
func (c *client) GetTags(ctx context.Context, ref reference.Named) ([]string, error) {
repoEndpoint, err := newDefaultRepositoryEndpoint(ref, c.insecureRegistry)
if err != nil {
return nil, err
}
repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint)
if err != nil {
return nil, err
}
return repo.Tags(ctx).All(ctx)
}
func (c *client) getRepositoryForReference(ctx context.Context, ref reference.Named, repoEndpoint repositoryEndpoint) (distribution.Repository, error) { func (c *client) getRepositoryForReference(ctx context.Context, ref reference.Named, repoEndpoint repositoryEndpoint) (distribution.Repository, error) {
repoName, err := reference.WithName(repoEndpoint.Name()) repoName, err := reference.WithName(repoEndpoint.Name())
if err != nil { if err != nil {
@ -207,16 +192,3 @@ func getManifestOptionsFromReference(ref reference.Named) (digest.Digest, []dist
} }
return "", nil, errors.Errorf("%s no tag or digest", ref) return "", nil, errors.Errorf("%s no tag or digest", ref)
} }
// GetRegistryAuth returns the auth config given an input image
func GetRegistryAuth(ctx context.Context, resolver AuthConfigResolver, imageName string) (*types.AuthConfig, error) {
distributionRef, err := reference.ParseNormalizedNamed(imageName)
if err != nil {
return nil, fmt.Errorf("Failed to parse image name: %s: %s", imageName, err)
}
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, resolver, distributionRef.String())
if err != nil {
return nil, fmt.Errorf("Failed to get imgRefAndAuth: %s", err)
}
return imgRefAndAuth.AuthConfig(), nil
}

@ -3,7 +3,6 @@ package client
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"github.com/docker/cli/cli/manifest/types" "github.com/docker/cli/cli/manifest/types"
"github.com/docker/distribution" "github.com/docker/distribution"
@ -14,7 +13,7 @@ import (
v2 "github.com/docker/distribution/registry/api/v2" v2 "github.com/docker/distribution/registry/api/v2"
distclient "github.com/docker/distribution/registry/client" distclient "github.com/docker/distribution/registry/client"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
digest "github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -128,7 +127,7 @@ func validateManifestDigest(ref reference.Named, mfst distribution.Manifest) (oc
// If pull by digest, then verify the manifest digest. // If pull by digest, then verify the manifest digest.
if digested, isDigested := ref.(reference.Canonical); isDigested { if digested, isDigested := ref.(reference.Canonical); isDigested {
if digested.Digest() != desc.Digest { if digested.Digest() != desc.Digest {
err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest()) err := errors.Errorf("manifest verification failed for digest %s", digested.Digest())
return ocispec.Descriptor{}, err return ocispec.Descriptor{}, err
} }
} }
@ -156,7 +155,7 @@ func pullManifestList(ctx context.Context, ref reference.Named, repo distributio
} }
v, ok := manifest.(*schema2.DeserializedManifest) v, ok := manifest.(*schema2.DeserializedManifest)
if !ok { if !ok {
return nil, fmt.Errorf("unsupported manifest format: %v", v) return nil, errors.Errorf("unsupported manifest format: %v", v)
} }
manifestRef, err := reference.WithDigest(ref, manifestDescriptor.Digest) manifestRef, err := reference.WithDigest(ref, manifestDescriptor.Digest)
@ -278,27 +277,17 @@ func allEndpoints(namedRef reference.Named, insecure bool) ([]registry.APIEndpoi
return endpoints, err return endpoints, err
} }
type notFoundError struct { func newNotFoundError(ref string) *notFoundError {
object string return &notFoundError{err: errors.New("no such manifest: " + ref)}
} }
func newNotFoundError(ref string) *notFoundError { type notFoundError struct {
return &notFoundError{object: ref} err error
} }
func (n *notFoundError) Error() string { func (n *notFoundError) Error() string {
return fmt.Sprintf("no such manifest: %s", n.object) return n.err.Error()
} }
// NotFound interface // NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
func (n *notFoundError) NotFound() {} func (n *notFoundError) NotFound() {}
// IsNotFound returns true if the error is a not found error
func IsNotFound(err error) bool {
_, ok := err.(notFound)
return ok
}
type notFound interface {
NotFound()
}

@ -99,7 +99,7 @@ func ExactArgs(number int) cobra.PositionalArgs {
} }
} }
//nolint: unparam //nolint:unparam
func pluralize(word string, number int) string { func pluralize(word string, number int) string {
if number == 1 { if number == 1 {
return word return word

@ -12,7 +12,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth"
"github.com/docker/distribution/registry/client/auth/challenge" "github.com/docker/distribution/registry/client/auth/challenge"
@ -21,7 +21,7 @@ import (
registrytypes "github.com/docker/docker/api/types/registry" registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
digest "github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/theupdateframework/notary" "github.com/theupdateframework/notary"
@ -47,7 +47,7 @@ var (
// GetTrustDirectory returns the base trust directory name // GetTrustDirectory returns the base trust directory name
func GetTrustDirectory() string { func GetTrustDirectory() string {
return filepath.Join(cliconfig.Dir(), "trust") return filepath.Join(config.Dir(), "trust")
} }
// certificateDirectory returns the directory containing // certificateDirectory returns the directory containing
@ -59,7 +59,7 @@ func certificateDirectory(server string) (string, error) {
return "", err return "", err
} }
return filepath.Join(cliconfig.Dir(), "tls", u.Host), nil return filepath.Join(config.Dir(), "tls", u.Host), nil
} }
// Server returns the base URL for the trust server. // Server returns the base URL for the trust server.

@ -6,12 +6,12 @@ import (
// ParseEnvFile reads a file with environment variables enumerated by lines // ParseEnvFile reads a file with environment variables enumerated by lines
// //
// ``Environment variable names used by the utilities in the Shell and // Environment variable names used by the utilities in the Shell and
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase // Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
// letters, digits, and the '_' (underscore) from the characters defined in // letters, digits, and the '_' (underscore) from the characters defined in
// Portable Character Set and do not begin with a digit. *But*, other // Portable Character Set and do not begin with a digit. *But*, other
// characters may be permitted by an implementation; applications shall // characters may be permitted by an implementation; applications shall
// tolerate the presence of such names.'' // tolerate the presence of such names.
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html // -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
// //
// As of #16585, it's up to application inside docker to validate or not // As of #16585, it's up to application inside docker to validate or not

@ -24,7 +24,8 @@ func parseCount(s string) (int, error) {
} }
// Set a new mount value // Set a new mount value
// nolint: gocyclo //
//nolint:gocyclo
func (o *GpuOpts) Set(value string) error { func (o *GpuOpts) Set(value string) error {
csvReader := csv.NewReader(strings.NewReader(value)) csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read() fields, err := csvReader.Read()

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package opts package opts

@ -1,5 +1,3 @@
// +build windows
package opts package opts
// defaultHost constant defines the default host string used by docker on Windows // defaultHost constant defines the default host string used by docker on Windows

@ -4,6 +4,7 @@ import (
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
@ -17,7 +18,8 @@ type MountOpt struct {
} }
// Set a new mount value // Set a new mount value
// nolint: gocyclo //
//nolint:gocyclo
func (m *MountOpt) Set(value string) error { func (m *MountOpt) Set(value string) error {
csvReader := csv.NewReader(strings.NewReader(value)) csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read() fields, err := csvReader.Read()
@ -92,6 +94,11 @@ func (m *MountOpt) Set(value string) error {
mount.Type = mounttypes.Type(strings.ToLower(value)) mount.Type = mounttypes.Type(strings.ToLower(value))
case "source", "src": case "source", "src":
mount.Source = value mount.Source = value
if strings.HasPrefix(value, "."+string(filepath.Separator)) || value == "." {
if abs, err := filepath.Abs(value); err == nil {
mount.Source = abs
}
}
case "target", "dst", "destination": case "target", "dst", "destination":
mount.Target = value mount.Target = value
case "readonly", "ro": case "readonly", "ro":

@ -55,7 +55,9 @@ func ConvertKVStringsToMap(values []string) map[string]string {
// ConvertKVStringsToMapWithNil converts ["key=value"] to {"key":"value"} // ConvertKVStringsToMapWithNil converts ["key=value"] to {"key":"value"}
// but set unset keys to nil - meaning the ones with no "=" in them. // but set unset keys to nil - meaning the ones with no "=" in them.
// We use this in cases where we need to distinguish between // We use this in cases where we need to distinguish between
// FOO= and FOO //
// FOO= and FOO
//
// where the latter case just means FOO was mentioned but not given a value // where the latter case just means FOO was mentioned but not given a value
func ConvertKVStringsToMapWithNil(values []string) map[string]*string { func ConvertKVStringsToMapWithNil(values []string) map[string]*string {
result := make(map[string]*string, len(values)) result := make(map[string]*string, len(values))

@ -26,7 +26,8 @@ type PortOpt struct {
} }
// Set a new port value // Set a new port value
// nolint: gocyclo //
//nolint:gocyclo
func (p *PortOpt) Set(value string) error { func (p *PortOpt) Set(value string) error {
longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value) longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
if err != nil { if err != nil {

@ -22,6 +22,9 @@ func (s *QuotedString) String() string {
} }
func trimQuotes(value string) string { func trimQuotes(value string) string {
if len(value) < 2 {
return value
}
lastIndex := len(value) - 1 lastIndex := len(value) - 1
for _, char := range []byte{'\'', '"'} { for _, char := range []byte{'\'', '"'} {
if value[0] == char && value[lastIndex] == char { if value[0] == char && value[lastIndex] == char {

@ -7,7 +7,7 @@ Aaron Feng <aaron.feng@gmail.com>
Aaron Hnatiw <aaron@griddio.com> Aaron Hnatiw <aaron@griddio.com>
Aaron Huslage <huslage@gmail.com> Aaron Huslage <huslage@gmail.com>
Aaron L. Xu <liker.xu@foxmail.com> Aaron L. Xu <liker.xu@foxmail.com>
Aaron Lehmann <aaron.lehmann@docker.com> Aaron Lehmann <alehmann@netflix.com>
Aaron Welch <welch@packet.net> Aaron Welch <welch@packet.net>
Aaron.L.Xu <likexu@harmonycloud.cn> Aaron.L.Xu <likexu@harmonycloud.cn>
Abel Muiño <amuino@gmail.com> Abel Muiño <amuino@gmail.com>
@ -61,10 +61,11 @@ Alan Scherger <flyinprogrammer@gmail.com>
Alan Thompson <cloojure@gmail.com> Alan Thompson <cloojure@gmail.com>
Albert Callarisa <shark234@gmail.com> Albert Callarisa <shark234@gmail.com>
Albert Zhang <zhgwenming@gmail.com> Albert Zhang <zhgwenming@gmail.com>
Albin Kerouanton <albin@akerouanton.name> Albin Kerouanton <albinker@gmail.com>
Alec Benson <albenson@redhat.com> Alec Benson <albenson@redhat.com>
Alejandro González Hevia <alejandrgh11@gmail.com> Alejandro González Hevia <alejandrgh11@gmail.com>
Aleksa Sarai <asarai@suse.de> Aleksa Sarai <asarai@suse.de>
Aleksandr Chebotov <v-aleche@microsoft.com>
Aleksandrs Fadins <aleks@s-ko.net> Aleksandrs Fadins <aleks@s-ko.net>
Alena Prokharchyk <alena@rancher.com> Alena Prokharchyk <alena@rancher.com>
Alessandro Boch <aboch@tetrationanalytics.com> Alessandro Boch <aboch@tetrationanalytics.com>
@ -76,6 +77,7 @@ Alex Crawford <alex.crawford@coreos.com>
Alex Ellis <alexellis2@gmail.com> Alex Ellis <alexellis2@gmail.com>
Alex Gaynor <alex.gaynor@gmail.com> Alex Gaynor <alex.gaynor@gmail.com>
Alex Goodman <wagoodman@gmail.com> Alex Goodman <wagoodman@gmail.com>
Alex Nordlund <alexander.nordlund@nasdaq.com>
Alex Olshansky <i@creagenics.com> Alex Olshansky <i@creagenics.com>
Alex Samorukov <samm@os2.kiev.ua> Alex Samorukov <samm@os2.kiev.ua>
Alex Warhawk <ax.warhawk@gmail.com> Alex Warhawk <ax.warhawk@gmail.com>
@ -83,7 +85,7 @@ Alexander Artemenko <svetlyak.40wt@gmail.com>
Alexander Boyd <alex@opengroove.org> Alexander Boyd <alex@opengroove.org>
Alexander Larsson <alexl@redhat.com> Alexander Larsson <alexl@redhat.com>
Alexander Midlash <amidlash@docker.com> Alexander Midlash <amidlash@docker.com>
Alexander Morozov <lk4d4@docker.com> Alexander Morozov <lk4d4math@gmail.com>
Alexander Polakov <plhk@sdf.org> Alexander Polakov <plhk@sdf.org>
Alexander Shopov <ash@kambanaria.org> Alexander Shopov <ash@kambanaria.org>
Alexandre Beslic <alexandre.beslic@gmail.com> Alexandre Beslic <alexandre.beslic@gmail.com>
@ -192,13 +194,15 @@ Antony Messerli <amesserl@rackspace.com>
Anuj Bahuguna <anujbahuguna.dev@gmail.com> Anuj Bahuguna <anujbahuguna.dev@gmail.com>
Anuj Varma <anujvarma@thumbtack.com> Anuj Varma <anujvarma@thumbtack.com>
Anusha Ragunathan <anusha.ragunathan@docker.com> Anusha Ragunathan <anusha.ragunathan@docker.com>
Anyu Wang <wanganyu@outlook.com>
apocas <petermdias@gmail.com> apocas <petermdias@gmail.com>
Arash Deshmeh <adeshmeh@ca.ibm.com> Arash Deshmeh <adeshmeh@ca.ibm.com>
ArikaChen <eaglesora@gmail.com> ArikaChen <eaglesora@gmail.com>
Arko Dasgupta <arko.dasgupta@docker.com> Arko Dasgupta <arko@tetrate.io>
Arnaud Lefebvre <a.lefebvre@outlook.fr> Arnaud Lefebvre <a.lefebvre@outlook.fr>
Arnaud Porterie <arnaud.porterie@docker.com> Arnaud Porterie <icecrime@gmail.com>
Arnaud Rebillout <arnaud.rebillout@collabora.com> Arnaud Rebillout <arnaud.rebillout@collabora.com>
Artem Khramov <akhramov@pm.me>
Arthur Barr <arthur.barr@uk.ibm.com> Arthur Barr <arthur.barr@uk.ibm.com>
Arthur Gautier <baloo@gandi.net> Arthur Gautier <baloo@gandi.net>
Artur Meyster <arthurfbi@yahoo.com> Artur Meyster <arthurfbi@yahoo.com>
@ -250,6 +254,7 @@ Billy Ridgway <wrridgwa@us.ibm.com>
Bily Zhang <xcoder@tenxcloud.com> Bily Zhang <xcoder@tenxcloud.com>
Bin Liu <liubin0329@gmail.com> Bin Liu <liubin0329@gmail.com>
Bingshen Wang <bingshen.wbs@alibaba-inc.com> Bingshen Wang <bingshen.wbs@alibaba-inc.com>
Bjorn Neergaard <bneergaard@mirantis.com>
Blake Geno <blakegeno@gmail.com> Blake Geno <blakegeno@gmail.com>
Boaz Shuster <ripcurld.github@gmail.com> Boaz Shuster <ripcurld.github@gmail.com>
bobby abbott <ttobbaybbob@gmail.com> bobby abbott <ttobbaybbob@gmail.com>
@ -343,6 +348,7 @@ Chen Qiu <cheney-90@hotmail.com>
Cheng-mean Liu <soccerl@microsoft.com> Cheng-mean Liu <soccerl@microsoft.com>
Chengfei Shang <cfshang@alauda.io> Chengfei Shang <cfshang@alauda.io>
Chengguang Xu <cgxu519@gmx.com> Chengguang Xu <cgxu519@gmx.com>
Chenyang Yan <memory.yancy@gmail.com>
chenyuzhu <chenyuzhi@oschina.cn> chenyuzhu <chenyuzhi@oschina.cn>
Chetan Birajdar <birajdar.chetan@gmail.com> Chetan Birajdar <birajdar.chetan@gmail.com>
Chewey <prosto-chewey@users.noreply.github.com> Chewey <prosto-chewey@users.noreply.github.com>
@ -406,20 +412,23 @@ Colin Walters <walters@verbum.org>
Collin Guarino <collin.guarino@gmail.com> Collin Guarino <collin.guarino@gmail.com>
Colm Hally <colmhally@gmail.com> Colm Hally <colmhally@gmail.com>
companycy <companycy@gmail.com> companycy <companycy@gmail.com>
Conor Evans <coevans@tcd.ie>
Corbin Coleman <corbin.coleman@docker.com> Corbin Coleman <corbin.coleman@docker.com>
Corey Farrell <git@cfware.com> Corey Farrell <git@cfware.com>
Cory Forsyth <cory.forsyth@gmail.com> Cory Forsyth <cory.forsyth@gmail.com>
Cory Snider <csnider@mirantis.com>
cressie176 <github@stephen-cresswell.net> cressie176 <github@stephen-cresswell.net>
CrimsonGlory <CrimsonGlory@users.noreply.github.com>
Cristian Ariza <dev@cristianrz.com> Cristian Ariza <dev@cristianrz.com>
Cristian Staretu <cristian.staretu@gmail.com> Cristian Staretu <cristian.staretu@gmail.com>
cristiano balducci <cristiano.balducci@gmail.com> cristiano balducci <cristiano.balducci@gmail.com>
Cristina Yenyxe Gonzalez Garcia <cristina.yenyxe@gmail.com> Cristina Yenyxe Gonzalez Garcia <cristina.yenyxe@gmail.com>
Cruceru Calin-Cristian <crucerucalincristian@gmail.com> Cruceru Calin-Cristian <crucerucalincristian@gmail.com>
CUI Wei <ghostplant@qq.com> CUI Wei <ghostplant@qq.com>
cuishuang <imcusg@gmail.com>
Cuong Manh Le <cuong.manhle.vn@gmail.com> Cuong Manh Le <cuong.manhle.vn@gmail.com>
Cyprian Gracz <cyprian.gracz@micro-jumbo.eu> Cyprian Gracz <cyprian.gracz@micro-jumbo.eu>
Cyril F <cyrilf7x@gmail.com> Cyril F <cyrilf7x@gmail.com>
Da McGrady <dabkb@aol.com>
Daan van Berkel <daan.v.berkel.1980@gmail.com> Daan van Berkel <daan.v.berkel.1980@gmail.com>
Daehyeok Mun <daehyeok@gmail.com> Daehyeok Mun <daehyeok@gmail.com>
Dafydd Crosby <dtcrsby@gmail.com> Dafydd Crosby <dtcrsby@gmail.com>
@ -437,6 +446,7 @@ Dan Hirsch <thequux@upstandinghackers.com>
Dan Keder <dan.keder@gmail.com> Dan Keder <dan.keder@gmail.com>
Dan Levy <dan@danlevy.net> Dan Levy <dan@danlevy.net>
Dan McPherson <dmcphers@redhat.com> Dan McPherson <dmcphers@redhat.com>
Dan Plamadeala <cornul11@gmail.com>
Dan Stine <sw@stinemail.com> Dan Stine <sw@stinemail.com>
Dan Williams <me@deedubs.com> Dan Williams <me@deedubs.com>
Dani Hodovic <dani.hodovic@gmail.com> Dani Hodovic <dani.hodovic@gmail.com>
@ -457,6 +467,7 @@ Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
Daniel Nephin <dnephin@docker.com> Daniel Nephin <dnephin@docker.com>
Daniel Norberg <dano@spotify.com> Daniel Norberg <dano@spotify.com>
Daniel Nordberg <dnordberg@gmail.com> Daniel Nordberg <dnordberg@gmail.com>
Daniel P. Berrangé <berrange@redhat.com>
Daniel Robinson <gottagetmac@gmail.com> Daniel Robinson <gottagetmac@gmail.com>
Daniel S <dan.streby@gmail.com> Daniel S <dan.streby@gmail.com>
Daniel Sweet <danieljsweet@icloud.com> Daniel Sweet <danieljsweet@icloud.com>
@ -465,6 +476,7 @@ Daniel Watkins <daniel@daniel-watkins.co.uk>
Daniel X Moore <yahivin@gmail.com> Daniel X Moore <yahivin@gmail.com>
Daniel YC Lin <dlin.tw@gmail.com> Daniel YC Lin <dlin.tw@gmail.com>
Daniel Zhang <jmzwcn@gmail.com> Daniel Zhang <jmzwcn@gmail.com>
Daniele Rondina <geaaru@sabayonlinux.org>
Danny Berger <dpb587@gmail.com> Danny Berger <dpb587@gmail.com>
Danny Milosavljevic <dannym@scratchpost.org> Danny Milosavljevic <dannym@scratchpost.org>
Danny Yates <danny@codeaholics.org> Danny Yates <danny@codeaholics.org>
@ -530,7 +542,7 @@ Dennis Docter <dennis@d23.nl>
Derek <crq@kernel.org> Derek <crq@kernel.org>
Derek <crquan@gmail.com> Derek <crquan@gmail.com>
Derek Ch <denc716@gmail.com> Derek Ch <denc716@gmail.com>
Derek McGowan <derek@mcgstyle.net> Derek McGowan <derek@mcg.dev>
Deric Crago <deric.crago@gmail.com> Deric Crago <deric.crago@gmail.com>
Deshi Xiao <dxiao@redhat.com> Deshi Xiao <dxiao@redhat.com>
devmeyster <arthurfbi@yahoo.com> devmeyster <arthurfbi@yahoo.com>
@ -550,9 +562,11 @@ Dimitris Rozakis <dimrozakis@gmail.com>
Dimitry Andric <d.andric@activevideo.com> Dimitry Andric <d.andric@activevideo.com>
Dinesh Subhraveti <dineshs@altiscale.com> Dinesh Subhraveti <dineshs@altiscale.com>
Ding Fei <dingfei@stars.org.cn> Ding Fei <dingfei@stars.org.cn>
dingwei <dingwei@cmss.chinamobile.com>
Diogo Monica <diogo@docker.com> Diogo Monica <diogo@docker.com>
DiuDiugirl <sophia.wang@pku.edu.cn> DiuDiugirl <sophia.wang@pku.edu.cn>
Djibril Koné <kone.djibril@gmail.com> Djibril Koné <kone.djibril@gmail.com>
Djordje Lukic <djordje.lukic@docker.com>
dkumor <daniel@dkumor.com> dkumor <daniel@dkumor.com>
Dmitri Logvinenko <dmitri.logvinenko@gmail.com> Dmitri Logvinenko <dmitri.logvinenko@gmail.com>
Dmitri Shuralyov <shurcooL@gmail.com> Dmitri Shuralyov <shurcooL@gmail.com>
@ -601,6 +615,7 @@ Elango Sivanandam <elango.siva@docker.com>
Elena Morozova <lelenanam@gmail.com> Elena Morozova <lelenanam@gmail.com>
Eli Uriegas <seemethere101@gmail.com> Eli Uriegas <seemethere101@gmail.com>
Elias Faxö <elias.faxo@tre.se> Elias Faxö <elias.faxo@tre.se>
Elias Koromilas <elias.koromilas@gmail.com>
Elias Probst <mail@eliasprobst.eu> Elias Probst <mail@eliasprobst.eu>
Elijah Zupancic <elijah@zupancic.name> Elijah Zupancic <elijah@zupancic.name>
eluck <mail@eluck.me> eluck <mail@eluck.me>
@ -610,6 +625,7 @@ Emil Hernvall <emil@quench.at>
Emily Maier <emily@emilymaier.net> Emily Maier <emily@emilymaier.net>
Emily Rose <emily@contactvibe.com> Emily Rose <emily@contactvibe.com>
Emir Ozer <emirozer@yandex.com> Emir Ozer <emirozer@yandex.com>
Eng Zer Jun <engzerjun@gmail.com>
Enguerran <engcolson@gmail.com> Enguerran <engcolson@gmail.com>
Eohyung Lee <liquidnuker@gmail.com> Eohyung Lee <liquidnuker@gmail.com>
epeterso <epeterson@breakpoint-labs.com> epeterso <epeterson@breakpoint-labs.com>
@ -724,11 +740,14 @@ Frederik Loeffert <frederik@zitrusmedia.de>
Frederik Nordahl Jul Sabroe <frederikns@gmail.com> Frederik Nordahl Jul Sabroe <frederikns@gmail.com>
Freek Kalter <freek@kalteronline.org> Freek Kalter <freek@kalteronline.org>
Frieder Bluemle <frieder.bluemle@gmail.com> Frieder Bluemle <frieder.bluemle@gmail.com>
frobnicaty <92033765+frobnicaty@users.noreply.github.com>
Frédéric Dalleau <frederic.dalleau@docker.com>
Fu JinLin <withlin@yeah.net> Fu JinLin <withlin@yeah.net>
Félix Baylac-Jacqué <baylac.felix@gmail.com> Félix Baylac-Jacqué <baylac.felix@gmail.com>
Félix Cantournet <felix.cantournet@cloudwatt.com> Félix Cantournet <felix.cantournet@cloudwatt.com>
Gabe Rosenhouse <gabe@missionst.com> Gabe Rosenhouse <gabe@missionst.com>
Gabor Nagy <mail@aigeruth.hu> Gabor Nagy <mail@aigeruth.hu>
Gabriel Goller <gabrielgoller123@gmail.com>
Gabriel L. Somlo <gsomlo@gmail.com> Gabriel L. Somlo <gsomlo@gmail.com>
Gabriel Linder <linder.gabriel@gmail.com> Gabriel Linder <linder.gabriel@gmail.com>
Gabriel Monroy <gabriel@opdemand.com> Gabriel Monroy <gabriel@opdemand.com>
@ -751,6 +770,7 @@ George Kontridze <george@bugsnag.com>
George MacRorie <gmacr31@gmail.com> George MacRorie <gmacr31@gmail.com>
George Xie <georgexsh@gmail.com> George Xie <georgexsh@gmail.com>
Georgi Hristozov <georgi@forkbomb.nl> Georgi Hristozov <georgi@forkbomb.nl>
Georgy Yakovlev <gyakovlev@gentoo.org>
Gereon Frey <gereon.frey@dynport.de> Gereon Frey <gereon.frey@dynport.de>
German DZ <germ@ndz.com.ar> German DZ <germ@ndz.com.ar>
Gert van Valkenhoef <g.h.m.van.valkenhoef@rug.nl> Gert van Valkenhoef <g.h.m.van.valkenhoef@rug.nl>
@ -762,6 +782,7 @@ Gildas Cuisinier <gildas.cuisinier@gcuisinier.net>
Giovan Isa Musthofa <giovanism@outlook.co.id> Giovan Isa Musthofa <giovanism@outlook.co.id>
gissehel <public-devgit-dantus@gissehel.org> gissehel <public-devgit-dantus@gissehel.org>
Giuseppe Mazzotta <gdm85@users.noreply.github.com> Giuseppe Mazzotta <gdm85@users.noreply.github.com>
Giuseppe Scrivano <gscrivan@redhat.com>
Gleb Fotengauer-Malinovskiy <glebfm@altlinux.org> Gleb Fotengauer-Malinovskiy <glebfm@altlinux.org>
Gleb M Borisov <borisov.gleb@gmail.com> Gleb M Borisov <borisov.gleb@gmail.com>
Glyn Normington <gnormington@gopivotal.com> Glyn Normington <gnormington@gopivotal.com>
@ -785,6 +806,7 @@ Guilherme Salgado <gsalgado@gmail.com>
Guillaume Dufour <gdufour.prestataire@voyages-sncf.com> Guillaume Dufour <gdufour.prestataire@voyages-sncf.com>
Guillaume J. Charmes <guillaume.charmes@docker.com> Guillaume J. Charmes <guillaume.charmes@docker.com>
Gunadhya S. <6939749+gunadhya@users.noreply.github.com> Gunadhya S. <6939749+gunadhya@users.noreply.github.com>
Guoqiang QI <guoqiang.qi1@gmail.com>
guoxiuyan <guoxiuyan@huawei.com> guoxiuyan <guoxiuyan@huawei.com>
Guri <odg0318@gmail.com> Guri <odg0318@gmail.com>
Gurjeet Singh <gurjeet@singh.im> Gurjeet Singh <gurjeet@singh.im>
@ -794,6 +816,7 @@ gwx296173 <gaojing3@huawei.com>
Günter Zöchbauer <guenter@gzoechbauer.com> Günter Zöchbauer <guenter@gzoechbauer.com>
Haichao Yang <yang.haichao@zte.com.cn> Haichao Yang <yang.haichao@zte.com.cn>
haikuoliu <haikuo@amazon.com> haikuoliu <haikuo@amazon.com>
haining.cao <haining.cao@daocloud.io>
Hakan Özler <hakan.ozler@kodcu.com> Hakan Özler <hakan.ozler@kodcu.com>
Hamish Hutchings <moredhel@aoeu.me> Hamish Hutchings <moredhel@aoeu.me>
Hannes Ljungberg <hannes@5monkeys.se> Hannes Ljungberg <hannes@5monkeys.se>
@ -889,6 +912,7 @@ Jake Champlin <jake.champlin.27@gmail.com>
Jake Moshenko <jake@devtable.com> Jake Moshenko <jake@devtable.com>
Jake Sanders <jsand@google.com> Jake Sanders <jsand@google.com>
Jakub Drahos <jdrahos@pulsepoint.com> Jakub Drahos <jdrahos@pulsepoint.com>
Jakub Guzik <jakubmguzik@gmail.com>
James Allen <jamesallen0108@gmail.com> James Allen <jamesallen0108@gmail.com>
James Carey <jecarey@us.ibm.com> James Carey <jecarey@us.ibm.com>
James Carr <james.r.carr@gmail.com> James Carr <james.r.carr@gmail.com>
@ -900,10 +924,12 @@ James Lal <james@lightsofapollo.com>
James Mills <prologic@shortcircuit.net.au> James Mills <prologic@shortcircuit.net.au>
James Nesbitt <jnesbitt@mirantis.com> James Nesbitt <jnesbitt@mirantis.com>
James Nugent <james@jen20.com> James Nugent <james@jen20.com>
James Sanders <james3sanders@gmail.com>
James Turnbull <james@lovedthanlost.net> James Turnbull <james@lovedthanlost.net>
James Watkins-Harvey <jwatkins@progi-media.com> James Watkins-Harvey <jwatkins@progi-media.com>
Jamie Hannaford <jamie@limetree.org> Jamie Hannaford <jamie@limetree.org>
Jamshid Afshar <jafshar@yahoo.com> Jamshid Afshar <jafshar@yahoo.com>
Jan Breig <git@pygos.space>
Jan Chren <dev.rindeal@gmail.com> Jan Chren <dev.rindeal@gmail.com>
Jan Keromnes <janx@linux.com> Jan Keromnes <janx@linux.com>
Jan Koprowski <jan.koprowski@gmail.com> Jan Koprowski <jan.koprowski@gmail.com>
@ -932,10 +958,11 @@ Jason Shepherd <jason@jasonshepherd.net>
Jason Smith <jasonrichardsmith@gmail.com> Jason Smith <jasonrichardsmith@gmail.com>
Jason Sommer <jsdirv@gmail.com> Jason Sommer <jsdirv@gmail.com>
Jason Stangroome <jason@codeassassin.com> Jason Stangroome <jason@codeassassin.com>
Javier Bassi <javierbassi@gmail.com>
jaxgeller <jacksongeller@gmail.com> jaxgeller <jacksongeller@gmail.com>
Jay <imjching@hotmail.com>
Jay <teguhwpurwanto@gmail.com> Jay <teguhwpurwanto@gmail.com>
Jay Kamat <github@jgkamat.33mail.com> Jay Kamat <github@jgkamat.33mail.com>
Jay Lim <jay@imjching.com>
Jean Rouge <rougej+github@gmail.com> Jean Rouge <rougej+github@gmail.com>
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com> Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com> Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
@ -1100,6 +1127,7 @@ Justas Brazauskas <brazauskasjustas@gmail.com>
Justen Martin <jmart@the-coder.com> Justen Martin <jmart@the-coder.com>
Justin Cormack <justin.cormack@docker.com> Justin Cormack <justin.cormack@docker.com>
Justin Force <justin.force@gmail.com> Justin Force <justin.force@gmail.com>
Justin Keller <85903732+jk-vb@users.noreply.github.com>
Justin Menga <justin.menga@gmail.com> Justin Menga <justin.menga@gmail.com>
Justin Plock <jplock@users.noreply.github.com> Justin Plock <jplock@users.noreply.github.com>
Justin Simonelis <justin.p.simonelis@gmail.com> Justin Simonelis <justin.p.simonelis@gmail.com>
@ -1148,6 +1176,7 @@ Kenjiro Nakayama <nakayamakenjiro@gmail.com>
Kent Johnson <kentoj@gmail.com> Kent Johnson <kentoj@gmail.com>
Kenta Tada <Kenta.Tada@sony.com> Kenta Tada <Kenta.Tada@sony.com>
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com> Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
Kevin Alvarez <crazy-max@users.noreply.github.com>
Kevin Burke <kev@inburke.com> Kevin Burke <kev@inburke.com>
Kevin Clark <kevin.clark@gmail.com> Kevin Clark <kevin.clark@gmail.com>
Kevin Feyrer <kevin.feyrer@btinternet.com> Kevin Feyrer <kevin.feyrer@btinternet.com>
@ -1174,6 +1203,7 @@ knappe <tyler.knappe@gmail.com>
Kohei Tsuruta <coheyxyz@gmail.com> Kohei Tsuruta <coheyxyz@gmail.com>
Koichi Shiraishi <k@zchee.io> Koichi Shiraishi <k@zchee.io>
Konrad Kleine <konrad.wilhelm.kleine@gmail.com> Konrad Kleine <konrad.wilhelm.kleine@gmail.com>
Konrad Ponichtera <konpon96@gmail.com>
Konstantin Gribov <grossws@gmail.com> Konstantin Gribov <grossws@gmail.com>
Konstantin L <sw.double@gmail.com> Konstantin L <sw.double@gmail.com>
Konstantin Pelykh <kpelykh@zettaset.com> Konstantin Pelykh <kpelykh@zettaset.com>
@ -1332,6 +1362,7 @@ Markus Fix <lispmeister@gmail.com>
Markus Kortlang <hyp3rdino@googlemail.com> Markus Kortlang <hyp3rdino@googlemail.com>
Martijn Dwars <ikben@martijndwars.nl> Martijn Dwars <ikben@martijndwars.nl>
Martijn van Oosterhout <kleptog@svana.org> Martijn van Oosterhout <kleptog@svana.org>
Martin Dojcak <martin.dojcak@lablabs.io>
Martin Honermeyer <maze@strahlungsfrei.de> Martin Honermeyer <maze@strahlungsfrei.de>
Martin Kelly <martin@surround.io> Martin Kelly <martin@surround.io>
Martin Mosegaard Amdisen <martin.amdisen@praqma.com> Martin Mosegaard Amdisen <martin.amdisen@praqma.com>
@ -1348,6 +1379,7 @@ Mathias Monnerville <mathias@monnerville.com>
Mathieu Champlon <mathieu.champlon@docker.com> Mathieu Champlon <mathieu.champlon@docker.com>
Mathieu Le Marec - Pasquet <kiorky@cryptelium.net> Mathieu Le Marec - Pasquet <kiorky@cryptelium.net>
Mathieu Parent <math.parent@gmail.com> Mathieu Parent <math.parent@gmail.com>
Mathieu Paturel <mathieu.paturel@gmail.com>
Matt Apperson <me@mattapperson.com> Matt Apperson <me@mattapperson.com>
Matt Bachmann <bachmann.matt@gmail.com> Matt Bachmann <bachmann.matt@gmail.com>
Matt Bajor <matt@notevenremotelydorky.com> Matt Bajor <matt@notevenremotelydorky.com>
@ -1356,6 +1388,7 @@ Matt Haggard <haggardii@gmail.com>
Matt Hoyle <matt@deployable.co> Matt Hoyle <matt@deployable.co>
Matt McCormick <matt.mccormick@kitware.com> Matt McCormick <matt.mccormick@kitware.com>
Matt Moore <mattmoor@google.com> Matt Moore <mattmoor@google.com>
Matt Morrison <3maven@gmail.com>
Matt Richardson <matt@redgumtech.com.au> Matt Richardson <matt@redgumtech.com.au>
Matt Rickard <mrick@google.com> Matt Rickard <mrick@google.com>
Matt Robenolt <matt@ydekproductions.com> Matt Robenolt <matt@ydekproductions.com>
@ -1400,7 +1433,7 @@ Michael Beskin <mrbeskin@gmail.com>
Michael Bridgen <mikeb@squaremobius.net> Michael Bridgen <mikeb@squaremobius.net>
Michael Brown <michael@netdirect.ca> Michael Brown <michael@netdirect.ca>
Michael Chiang <mchiang@docker.com> Michael Chiang <mchiang@docker.com>
Michael Crosby <michael@docker.com> Michael Crosby <crosbymichael@gmail.com>
Michael Currie <mcurrie@bruceforceresearch.com> Michael Currie <mcurrie@bruceforceresearch.com>
Michael Friis <friism@gmail.com> Michael Friis <friism@gmail.com>
Michael Gorsuch <gorsuch@github.com> Michael Gorsuch <gorsuch@github.com>
@ -1409,6 +1442,7 @@ Michael Holzheu <holzheu@linux.vnet.ibm.com>
Michael Hudson-Doyle <michael.hudson@canonical.com> Michael Hudson-Doyle <michael.hudson@canonical.com>
Michael Huettermann <michael@huettermann.net> Michael Huettermann <michael@huettermann.net>
Michael Irwin <mikesir87@gmail.com> Michael Irwin <mikesir87@gmail.com>
Michael Kuehn <micha@kuehn.io>
Michael Käufl <docker@c.michael-kaeufl.de> Michael Käufl <docker@c.michael-kaeufl.de>
Michael Neale <michael.neale@gmail.com> Michael Neale <michael.neale@gmail.com>
Michael Nussbaum <michael.nussbaum@getbraintree.com> Michael Nussbaum <michael.nussbaum@getbraintree.com>
@ -1418,6 +1452,7 @@ Michael Spetsiotis <michael_spets@hotmail.com>
Michael Stapelberg <michael+gh@stapelberg.de> Michael Stapelberg <michael+gh@stapelberg.de>
Michael Steinert <mike.steinert@gmail.com> Michael Steinert <mike.steinert@gmail.com>
Michael Thies <michaelthies78@gmail.com> Michael Thies <michaelthies78@gmail.com>
Michael Weidmann <michaelweidmann@web.de>
Michael West <mwest@mdsol.com> Michael West <mwest@mdsol.com>
Michael Zhao <michael.zhao@arm.com> Michael Zhao <michael.zhao@arm.com>
Michal Fojtik <mfojtik@redhat.com> Michal Fojtik <mfojtik@redhat.com>
@ -1458,6 +1493,7 @@ Mike Snitzer <snitzer@redhat.com>
mikelinjie <294893458@qq.com> mikelinjie <294893458@qq.com>
Mikhail Sobolev <mss@mawhrin.net> Mikhail Sobolev <mss@mawhrin.net>
Miklos Szegedi <miklos.szegedi@cloudera.com> Miklos Szegedi <miklos.szegedi@cloudera.com>
Milas Bowman <milasb@gmail.com>
Milind Chawre <milindchawre@gmail.com> Milind Chawre <milindchawre@gmail.com>
Miloslav Trmač <mitr@redhat.com> Miloslav Trmač <mitr@redhat.com>
mingqing <limingqing@cyou-inc.com> mingqing <limingqing@cyou-inc.com>
@ -1533,6 +1569,7 @@ Nicolas Kaiser <nikai@nikai.net>
Nicolas Sterchele <sterchele.nicolas@gmail.com> Nicolas Sterchele <sterchele.nicolas@gmail.com>
Nicolas V Castet <nvcastet@us.ibm.com> Nicolas V Castet <nvcastet@us.ibm.com>
Nicolás Hock Isaza <nhocki@gmail.com> Nicolás Hock Isaza <nhocki@gmail.com>
Niel Drummond <niel@drummond.lu>
Nigel Poulton <nigelpoulton@hotmail.com> Nigel Poulton <nigelpoulton@hotmail.com>
Nik Nyby <nikolas@gnu.org> Nik Nyby <nikolas@gnu.org>
Nikhil Chawla <chawlanikhil24@gmail.com> Nikhil Chawla <chawlanikhil24@gmail.com>
@ -1614,6 +1651,7 @@ Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Pavlos Ratis <dastergon@gentoo.org> Pavlos Ratis <dastergon@gentoo.org>
Pavol Vargovcik <pallly.vargovcik@gmail.com> Pavol Vargovcik <pallly.vargovcik@gmail.com>
Pawel Konczalski <mail@konczalski.de> Pawel Konczalski <mail@konczalski.de>
Paweł Gronowski <pawel.gronowski@docker.com>
Peeyush Gupta <gpeeyush@linux.vnet.ibm.com> Peeyush Gupta <gpeeyush@linux.vnet.ibm.com>
Peggy Li <peggyli.224@gmail.com> Peggy Li <peggyli.224@gmail.com>
Pei Su <sillyousu@gmail.com> Pei Su <sillyousu@gmail.com>
@ -1621,6 +1659,7 @@ Peng Tao <bergwolf@gmail.com>
Penghan Wang <ph.wang@daocloud.io> Penghan Wang <ph.wang@daocloud.io>
Per Weijnitz <per.weijnitz@gmail.com> Per Weijnitz <per.weijnitz@gmail.com>
perhapszzy@sina.com <perhapszzy@sina.com> perhapszzy@sina.com <perhapszzy@sina.com>
Pete Woods <pete.woods@circleci.com>
Peter Bourgon <peter@bourgon.org> Peter Bourgon <peter@bourgon.org>
Peter Braden <peterbraden@peterbraden.co.uk> Peter Braden <peterbraden@peterbraden.co.uk>
Peter Bücker <peter.buecker@pressrelations.de> Peter Bücker <peter.buecker@pressrelations.de>
@ -1638,7 +1677,7 @@ Peter Waller <p@pwaller.net>
Petr Švihlík <svihlik.petr@gmail.com> Petr Švihlík <svihlik.petr@gmail.com>
Petros Angelatos <petrosagg@gmail.com> Petros Angelatos <petrosagg@gmail.com>
Phil <underscorephil@gmail.com> Phil <underscorephil@gmail.com>
Phil Estes <estesp@linux.vnet.ibm.com> Phil Estes <estesp@gmail.com>
Phil Spitler <pspitler@gmail.com> Phil Spitler <pspitler@gmail.com>
Philip Alexander Etling <paetling@gmail.com> Philip Alexander Etling <paetling@gmail.com>
Philip Monroe <phil@philmonroe.com> Philip Monroe <phil@philmonroe.com>
@ -1707,6 +1746,7 @@ Renaud Gaubert <rgaubert@nvidia.com>
Rhys Hiltner <rhys@twitch.tv> Rhys Hiltner <rhys@twitch.tv>
Ri Xu <xuri.me@gmail.com> Ri Xu <xuri.me@gmail.com>
Ricardo N Feliciano <FelicianoTech@gmail.com> Ricardo N Feliciano <FelicianoTech@gmail.com>
Rich Horwood <rjhorwood@apple.com>
Rich Moyse <rich@moyse.us> Rich Moyse <rich@moyse.us>
Rich Seymour <rseymour@gmail.com> Rich Seymour <rseymour@gmail.com>
Richard <richard.scothern@gmail.com> Richard <richard.scothern@gmail.com>
@ -1731,6 +1771,7 @@ Robert Bachmann <rb@robertbachmann.at>
Robert Bittle <guywithnose@gmail.com> Robert Bittle <guywithnose@gmail.com>
Robert Obryk <robryk@gmail.com> Robert Obryk <robryk@gmail.com>
Robert Schneider <mail@shakeme.info> Robert Schneider <mail@shakeme.info>
Robert Shade <robert.shade@gmail.com>
Robert Stern <lexandro2000@gmail.com> Robert Stern <lexandro2000@gmail.com>
Robert Terhaar <rterhaar@atlanticdynamic.com> Robert Terhaar <rterhaar@atlanticdynamic.com>
Robert Wallis <smilingrob@gmail.com> Robert Wallis <smilingrob@gmail.com>
@ -1743,6 +1784,7 @@ Robin Speekenbrink <robin@kingsquare.nl>
Robin Thoni <robin@rthoni.com> Robin Thoni <robin@rthoni.com>
robpc <rpcann@gmail.com> robpc <rpcann@gmail.com>
Rodolfo Carvalho <rhcarvalho@gmail.com> Rodolfo Carvalho <rhcarvalho@gmail.com>
Rodrigo Campos <rodrigo@kinvolk.io>
Rodrigo Vaz <rodrigo.vaz@gmail.com> Rodrigo Vaz <rodrigo.vaz@gmail.com>
Roel Van Nyen <roel.vannyen@gmail.com> Roel Van Nyen <roel.vannyen@gmail.com>
Roger Peppe <rogpeppe@gmail.com> Roger Peppe <rogpeppe@gmail.com>
@ -1757,6 +1799,8 @@ Roma Sokolov <sokolov.r.v@gmail.com>
Roman Dudin <katrmr@gmail.com> Roman Dudin <katrmr@gmail.com>
Roman Mazur <roman@balena.io> Roman Mazur <roman@balena.io>
Roman Strashkin <roman.strashkin@gmail.com> Roman Strashkin <roman.strashkin@gmail.com>
Roman Volosatovs <roman.volosatovs@docker.com>
Roman Zabaluev <gpg@haarolean.dev>
Ron Smits <ron.smits@gmail.com> Ron Smits <ron.smits@gmail.com>
Ron Williams <ron.a.williams@gmail.com> Ron Williams <ron.a.williams@gmail.com>
Rong Gao <gaoronggood@163.com> Rong Gao <gaoronggood@163.com>
@ -1782,6 +1826,7 @@ Russ Magee <rmagee@gmail.com>
Ryan Abrams <rdabrams@gmail.com> Ryan Abrams <rdabrams@gmail.com>
Ryan Anderson <anderson.ryanc@gmail.com> Ryan Anderson <anderson.ryanc@gmail.com>
Ryan Aslett <github@mixologic.com> Ryan Aslett <github@mixologic.com>
Ryan Barry <rbarry@mirantis.com>
Ryan Belgrave <rmb1993@gmail.com> Ryan Belgrave <rmb1993@gmail.com>
Ryan Campbell <campbellr@gmail.com> Ryan Campbell <campbellr@gmail.com>
Ryan Detzel <ryan.detzel@gmail.com> Ryan Detzel <ryan.detzel@gmail.com>
@ -1790,6 +1835,7 @@ Ryan Liu <ryanlyy@me.com>
Ryan McLaughlin <rmclaughlin@insidesales.com> Ryan McLaughlin <rmclaughlin@insidesales.com>
Ryan O'Donnell <odonnellryanc@gmail.com> Ryan O'Donnell <odonnellryanc@gmail.com>
Ryan Seto <ryanseto@yak.net> Ryan Seto <ryanseto@yak.net>
Ryan Shea <sheabot03@gmail.com>
Ryan Simmen <ryan.simmen@gmail.com> Ryan Simmen <ryan.simmen@gmail.com>
Ryan Stelly <ryan.stelly@live.com> Ryan Stelly <ryan.stelly@live.com>
Ryan Thomas <rthomas@atlassian.com> Ryan Thomas <rthomas@atlassian.com>
@ -1822,8 +1868,9 @@ Sambuddha Basu <sambuddhabasu1@gmail.com>
Sami Wagiaalla <swagiaal@redhat.com> Sami Wagiaalla <swagiaal@redhat.com>
Samuel Andaya <samuel@andaya.net> Samuel Andaya <samuel@andaya.net>
Samuel Dion-Girardeau <samuel.diongirardeau@gmail.com> Samuel Dion-Girardeau <samuel.diongirardeau@gmail.com>
Samuel Karp <skarp@amazon.com> Samuel Karp <me@samuelkarp.com>
Samuel PHAN <samuel-phan@users.noreply.github.com> Samuel PHAN <samuel-phan@users.noreply.github.com>
sanchayanghosh <sanchayanghosh@outlook.com>
Sandeep Bansal <sabansal@microsoft.com> Sandeep Bansal <sabansal@microsoft.com>
Sankar சங்கர் <sankar.curiosity@gmail.com> Sankar சங்கர் <sankar.curiosity@gmail.com>
Sanket Saurav <sanketsaurav@gmail.com> Sanket Saurav <sanketsaurav@gmail.com>
@ -1852,6 +1899,7 @@ Sean P. Kane <skane@newrelic.com>
Sean Rodman <srodman7689@gmail.com> Sean Rodman <srodman7689@gmail.com>
Sebastiaan van Steenis <mail@superseb.nl> Sebastiaan van Steenis <mail@superseb.nl>
Sebastiaan van Stijn <github@gone.nl> Sebastiaan van Stijn <github@gone.nl>
Sebastian Höffner <sebastian.hoeffner@mevis.fraunhofer.de>
Sebastian Radloff <sradloff23@gmail.com> Sebastian Radloff <sradloff23@gmail.com>
Sebastien Goasguen <runseb@gmail.com> Sebastien Goasguen <runseb@gmail.com>
Senthil Kumar Selvaraj <senthil.thecoder@gmail.com> Senthil Kumar Selvaraj <senthil.thecoder@gmail.com>
@ -1881,6 +1929,7 @@ Shengbo Song <thomassong@tencent.com>
Shengjing Zhu <zhsj@debian.org> Shengjing Zhu <zhsj@debian.org>
Shev Yan <yandong_8212@163.com> Shev Yan <yandong_8212@163.com>
Shih-Yuan Lee <fourdollars@gmail.com> Shih-Yuan Lee <fourdollars@gmail.com>
Shihao Xia <charlesxsh@hotmail.com>
Shijiang Wei <mountkin@gmail.com> Shijiang Wei <mountkin@gmail.com>
Shijun Qin <qinshijun16@mails.ucas.ac.cn> Shijun Qin <qinshijun16@mails.ucas.ac.cn>
Shishir Mahajan <shishir.mahajan@redhat.com> Shishir Mahajan <shishir.mahajan@redhat.com>
@ -1933,6 +1982,7 @@ Stefan S. <tronicum@user.github.com>
Stefan Scherer <stefan.scherer@docker.com> Stefan Scherer <stefan.scherer@docker.com>
Stefan Staudenmeyer <doerte@instana.com> Stefan Staudenmeyer <doerte@instana.com>
Stefan Weil <sw@weilnetz.de> Stefan Weil <sw@weilnetz.de>
Steffen Butzer <steffen.butzer@outlook.com>
Stephan Spindler <shutefan@gmail.com> Stephan Spindler <shutefan@gmail.com>
Stephen Benjamin <stephen@redhat.com> Stephen Benjamin <stephen@redhat.com>
Stephen Crosby <stevecrozz@gmail.com> Stephen Crosby <stevecrozz@gmail.com>
@ -1951,6 +2001,7 @@ Steven Iveson <sjiveson@outlook.com>
Steven Merrill <steven.merrill@gmail.com> Steven Merrill <steven.merrill@gmail.com>
Steven Richards <steven@axiomzen.co> Steven Richards <steven@axiomzen.co>
Steven Taylor <steven.taylor@me.com> Steven Taylor <steven.taylor@me.com>
Stéphane Este-Gracias <sestegra@gmail.com>
Stig Larsson <stig@larsson.dev> Stig Larsson <stig@larsson.dev>
Su Wang <su.wang@docker.com> Su Wang <su.wang@docker.com>
Subhajit Ghosh <isubuz.g@gmail.com> Subhajit Ghosh <isubuz.g@gmail.com>
@ -1962,12 +2013,13 @@ Sunny Gogoi <indiasuny000@gmail.com>
Suryakumar Sudar <surya.trunks@gmail.com> Suryakumar Sudar <surya.trunks@gmail.com>
Sven Dowideit <SvenDowideit@home.org.au> Sven Dowideit <SvenDowideit@home.org.au>
Swapnil Daingade <swapnil.daingade@gmail.com> Swapnil Daingade <swapnil.daingade@gmail.com>
Sylvain Baubeau <sbaubeau@redhat.com> Sylvain Baubeau <lebauce@gmail.com>
Sylvain Bellemare <sylvain@ascribe.io> Sylvain Bellemare <sylvain@ascribe.io>
Sébastien <sebastien@yoozio.com> Sébastien <sebastien@yoozio.com>
Sébastien HOUZÉ <cto@verylastroom.com> Sébastien HOUZÉ <cto@verylastroom.com>
Sébastien Luttringer <seblu@seblu.net> Sébastien Luttringer <seblu@seblu.net>
Sébastien Stormacq <sebsto@users.noreply.github.com> Sébastien Stormacq <sebsto@users.noreply.github.com>
Sören Tempel <soeren+git@soeren-tempel.net>
Tabakhase <mail@tabakhase.com> Tabakhase <mail@tabakhase.com>
Tadej Janež <tadej.j@nez.si> Tadej Janež <tadej.j@nez.si>
TAGOMORI Satoshi <tagomoris@gmail.com> TAGOMORI Satoshi <tagomoris@gmail.com>
@ -1996,6 +2048,7 @@ Thomas Gazagnaire <thomas@gazagnaire.org>
Thomas Graf <tgraf@suug.ch> Thomas Graf <tgraf@suug.ch>
Thomas Grainger <tagrain@gmail.com> Thomas Grainger <tagrain@gmail.com>
Thomas Hansen <thomas.hansen@gmail.com> Thomas Hansen <thomas.hansen@gmail.com>
Thomas Ledos <thomas.ledos92@gmail.com>
Thomas Leonard <thomas.leonard@docker.com> Thomas Leonard <thomas.leonard@docker.com>
Thomas Léveil <thomasleveil@gmail.com> Thomas Léveil <thomasleveil@gmail.com>
Thomas Orozco <thomas@orozco.fr> Thomas Orozco <thomas@orozco.fr>
@ -2064,9 +2117,11 @@ Tomas Tomecek <ttomecek@redhat.com>
Tomasz Kopczynski <tomek@kopczynski.net.pl> Tomasz Kopczynski <tomek@kopczynski.net.pl>
Tomasz Lipinski <tlipinski@users.noreply.github.com> Tomasz Lipinski <tlipinski@users.noreply.github.com>
Tomasz Nurkiewicz <nurkiewicz@gmail.com> Tomasz Nurkiewicz <nurkiewicz@gmail.com>
Tomek Mańko <tomek.manko@railgun-solutions.com>
Tommaso Visconti <tommaso.visconti@gmail.com> Tommaso Visconti <tommaso.visconti@gmail.com>
Tomoya Tabuchi <t@tomoyat1.com> Tomoya Tabuchi <t@tomoyat1.com>
Tomáš Hrčka <thrcka@redhat.com> Tomáš Hrčka <thrcka@redhat.com>
tonic <tonicbupt@gmail.com>
Tonny Xu <tonny.xu@gmail.com> Tonny Xu <tonny.xu@gmail.com>
Tony Abboud <tdabboud@hotmail.com> Tony Abboud <tdabboud@hotmail.com>
Tony Daws <tony@daws.ca> Tony Daws <tony@daws.ca>
@ -2087,6 +2142,7 @@ Trevor Sullivan <pcgeek86@gmail.com>
Trishna Guha <trishnaguha17@gmail.com> Trishna Guha <trishnaguha17@gmail.com>
Tristan Carel <tristan@cogniteev.com> Tristan Carel <tristan@cogniteev.com>
Troy Denton <trdenton@gmail.com> Troy Denton <trdenton@gmail.com>
Tudor Brindus <me@tbrindus.ca>
Ty Alexander <ty.alexander@sendgrid.com> Ty Alexander <ty.alexander@sendgrid.com>
Tycho Andersen <tycho@docker.com> Tycho Andersen <tycho@docker.com>
Tyler Brock <tyler.brock@gmail.com> Tyler Brock <tyler.brock@gmail.com>
@ -2118,7 +2174,7 @@ Viktor Stanchev <me@viktorstanchev.com>
Viktor Vojnovski <viktor.vojnovski@amadeus.com> Viktor Vojnovski <viktor.vojnovski@amadeus.com>
VinayRaghavanKS <raghavan.vinay@gmail.com> VinayRaghavanKS <raghavan.vinay@gmail.com>
Vincent Batts <vbatts@redhat.com> Vincent Batts <vbatts@redhat.com>
Vincent Bernat <Vincent.Bernat@exoscale.ch> Vincent Bernat <vincent@bernat.ch>
Vincent Boulineau <vincent.boulineau@datadoghq.com> Vincent Boulineau <vincent.boulineau@datadoghq.com>
Vincent Demeester <vincent.demeester@docker.com> Vincent Demeester <vincent.demeester@docker.com>
Vincent Giersch <vincent.giersch@ovh.net> Vincent Giersch <vincent.giersch@ovh.net>
@ -2196,6 +2252,7 @@ Wolfgang Powisch <powo@powo.priv.at>
Wonjun Kim <wonjun.kim@navercorp.com> Wonjun Kim <wonjun.kim@navercorp.com>
WuLonghui <wlh6666@qq.com> WuLonghui <wlh6666@qq.com>
xamyzhao <x.amy.zhao@gmail.com> xamyzhao <x.amy.zhao@gmail.com>
Xia Wu <xwumzn@amazon.com>
Xian Chaobo <xianchaobo@huawei.com> Xian Chaobo <xianchaobo@huawei.com>
Xianglin Gao <xlgao@zju.edu.cn> Xianglin Gao <xlgao@zju.edu.cn>
Xianjie <guxianjie@gmail.com> Xianjie <guxianjie@gmail.com>
@ -2220,6 +2277,7 @@ Xuecong Liao <satorulogic@gmail.com>
xuzhaokui <cynicholas@gmail.com> xuzhaokui <cynicholas@gmail.com>
Yadnyawalkya Tale <ytale@redhat.com> Yadnyawalkya Tale <ytale@redhat.com>
Yahya <ya7yaz@gmail.com> Yahya <ya7yaz@gmail.com>
yalpul <yalpul@gmail.com>
YAMADA Tsuyoshi <tyamada@minimum2scp.org> YAMADA Tsuyoshi <tyamada@minimum2scp.org>
Yamasaki Masahide <masahide.y@gmail.com> Yamasaki Masahide <masahide.y@gmail.com>
Yan Feng <yanfeng2@huawei.com> Yan Feng <yanfeng2@huawei.com>
@ -2254,6 +2312,7 @@ Yu-Ju Hong <yjhong@google.com>
Yuan Sun <sunyuan3@huawei.com> Yuan Sun <sunyuan3@huawei.com>
Yuanhong Peng <pengyuanhong@huawei.com> Yuanhong Peng <pengyuanhong@huawei.com>
Yue Zhang <zy675793960@yeah.net> Yue Zhang <zy675793960@yeah.net>
Yufei Xiong <yufei.xiong@qq.com>
Yuhao Fang <fangyuhao@gmail.com> Yuhao Fang <fangyuhao@gmail.com>
Yuichiro Kaneko <spiketeika@gmail.com> Yuichiro Kaneko <spiketeika@gmail.com>
YujiOshima <yuji.oshima0x3fd@gmail.com> YujiOshima <yuji.oshima0x3fd@gmail.com>

File diff suppressed because it is too large Load Diff

@ -112,10 +112,16 @@ type NetworkListOptions struct {
Filters filters.Args Filters filters.Args
} }
// NewHijackedResponse intializes a HijackedResponse type
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
}
// HijackedResponse holds connection information for a hijacked request. // HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct { type HijackedResponse struct {
Conn net.Conn mediaType string
Reader *bufio.Reader Conn net.Conn
Reader *bufio.Reader
} }
// Close closes the hijacked connection and reader. // Close closes the hijacked connection and reader.
@ -123,6 +129,15 @@ func (h *HijackedResponse) Close() {
h.Conn.Close() h.Conn.Close()
} }
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
// returns false if HTTP Content-Type is not relevant, and container must be inspected
func (h *HijackedResponse) MediaType() (string, bool) {
if h.mediaType == "" {
return "", false
}
return h.mediaType, true
}
// CloseWriter is an interface that implements structs // CloseWriter is an interface that implements structs
// that close input streams to prevent from writing. // that close input streams to prevent from writing.
type CloseWriter interface { type CloseWriter interface {

@ -33,6 +33,7 @@ type ExecConfig struct {
User string // User that will run the command User string // User that will run the command
Privileged bool // Is the container in privileged mode Privileged bool // Is the container in privileged mode
Tty bool // Attach standard streams to a tty. Tty bool // Attach standard streams to a tty.
ConsoleSize *[2]uint `json:",omitempty"` // Initial console size [height, width]
AttachStdin bool // Attach the standard input, makes possible user interaction AttachStdin bool // Attach the standard input, makes possible user interaction
AttachStderr bool // Attach the standard error AttachStderr bool // Attach the standard error
AttachStdout bool // Attach the standard output AttachStdout bool // Attach the standard output

@ -1,6 +1,7 @@
package container // import "github.com/docker/docker/api/types/container" package container // import "github.com/docker/docker/api/types/container"
import ( import (
"io"
"time" "time"
"github.com/docker/docker/api/types/strslice" "github.com/docker/docker/api/types/strslice"
@ -13,6 +14,24 @@ import (
// Docker interprets it as 3 nanoseconds. // Docker interprets it as 3 nanoseconds.
const MinimumDuration = 1 * time.Millisecond const MinimumDuration = 1 * time.Millisecond
// StopOptions holds the options to stop or restart a container.
type StopOptions struct {
// Signal (optional) is the signal to send to the container to (gracefully)
// stop it before forcibly terminating the container with SIGKILL after the
// timeout expires. If not value is set, the default (SIGTERM) is used.
Signal string `json:",omitempty"`
// Timeout (optional) is the timeout (in seconds) to wait for the container
// to stop gracefully before forcibly terminating it with SIGKILL.
//
// - Use nil to use the default timeout (10 seconds).
// - Use '-1' to wait indefinitely.
// - Use '0' to not wait for the container to exit gracefully, and
// immediately proceeds to forcibly terminating the container.
// - Other positive values are used as timeout (in seconds).
Timeout *int `json:",omitempty"`
}
// HealthConfig holds configuration settings for the HEALTHCHECK feature. // HealthConfig holds configuration settings for the HEALTHCHECK feature.
type HealthConfig struct { type HealthConfig struct {
// Test is the test to perform to check that the container is healthy. // Test is the test to perform to check that the container is healthy.
@ -34,6 +53,14 @@ type HealthConfig struct {
Retries int `json:",omitempty"` Retries int `json:",omitempty"`
} }
// ExecStartOptions holds the options to start container's exec.
type ExecStartOptions struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
ConsoleSize *[2]uint `json:",omitempty"`
}
// Config contains the configuration data about a container. // Config contains the configuration data about a container.
// It should hold only portable information about the container. // It should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on". // Here, "portable" means "independent from the host we are running on".

@ -1,20 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerCreateCreatedBody OK response to ContainerCreate operation
// swagger:model ContainerCreateCreatedBody
type ContainerCreateCreatedBody struct {
// The ID of the created container
// Required: true
ID string `json:"Id"`
// Warnings encountered when creating the container
// Required: true
Warnings []string `json:"Warnings"`
}

@ -1,28 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerWaitOKBodyError container waiting error, if any
// swagger:model ContainerWaitOKBodyError
type ContainerWaitOKBodyError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}
// ContainerWaitOKBody OK response to ContainerWait operation
// swagger:model ContainerWaitOKBody
type ContainerWaitOKBody struct {
// error
// Required: true
Error *ContainerWaitOKBodyError `json:"Error"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`
}

@ -0,0 +1,19 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// CreateResponse ContainerCreateResponse
//
// OK response to ContainerCreate operation
// swagger:model CreateResponse
type CreateResponse struct {
// The ID of the created container
// Required: true
ID string `json:"Id"`
// Warnings encountered when creating the container
// Required: true
Warnings []string `json:"Warnings"`
}

@ -0,0 +1,16 @@
package container // import "github.com/docker/docker/api/types/container"
// ContainerCreateCreatedBody OK response to ContainerCreate operation
//
// Deprecated: use CreateResponse
type ContainerCreateCreatedBody = CreateResponse
// ContainerWaitOKBody OK response to ContainerWait operation
//
// Deprecated: use WaitResponse
type ContainerWaitOKBody = WaitResponse
// ContainerWaitOKBodyError container waiting error, if any
//
// Deprecated: use WaitExitError
type ContainerWaitOKBodyError = WaitExitError

@ -376,14 +376,17 @@ type Resources struct {
Devices []DeviceMapping // List of devices to map inside the container Devices []DeviceMapping // List of devices to map inside the container
DeviceCgroupRules []string // List of rule to be added to the device cgroup DeviceCgroupRules []string // List of rule to be added to the device cgroup
DeviceRequests []DeviceRequest // List of device requests for device drivers DeviceRequests []DeviceRequest // List of device requests for device drivers
KernelMemory int64 // Kernel memory limit (in bytes), Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes) // KernelMemory specifies the kernel memory limit (in bytes) for the container.
MemoryReservation int64 // Memory soft limit (in bytes) // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes.
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap KernelMemory int64 `json:",omitempty"`
MemorySwappiness *int64 // Tuning container memory swappiness behaviour KernelMemoryTCP int64 `json:",omitempty"` // Hard limit for kernel TCP buffer memory (in bytes)
OomKillDisable *bool // Whether to disable OOM Killer or not MemoryReservation int64 // Memory soft limit (in bytes)
PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change. MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
Ulimits []*units.Ulimit // List of ulimits to be set in the container MemorySwappiness *int64 // Tuning container memory swappiness behaviour
OomKillDisable *bool // Whether to disable OOM Killer or not
PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
Ulimits []*units.Ulimit // List of ulimits to be set in the container
// Applicable to Windows // Applicable to Windows
CPUCount int64 `json:"CpuCount"` // CPU count CPUCount int64 `json:"CpuCount"` // CPU count
@ -414,6 +417,7 @@ type HostConfig struct {
AutoRemove bool // Automatically remove container when it exits AutoRemove bool // Automatically remove container when it exits
VolumeDriver string // Name of the volume driver used to mount volumes VolumeDriver string // Name of the volume driver used to mount volumes
VolumesFrom []string // List of volumes to take from other container VolumesFrom []string // List of volumes to take from other container
ConsoleSize [2]uint // Initial console size (height,width)
// Applicable to UNIX platforms // Applicable to UNIX platforms
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
@ -442,8 +446,7 @@ type HostConfig struct {
Runtime string `json:",omitempty"` // Runtime to use with this container Runtime string `json:",omitempty"` // Runtime to use with this container
// Applicable to Windows // Applicable to Windows
ConsoleSize [2]uint // Initial console size (height,width) Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
// Contains container's resources (cgroups, ulimits) // Contains container's resources (cgroups, ulimits)
Resources Resources

@ -0,0 +1,12 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// WaitExitError container waiting error, if any
// swagger:model WaitExitError
type WaitExitError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}

@ -0,0 +1,18 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// WaitResponse ContainerWaitResponse
//
// OK response to ContainerWait operation
// swagger:model WaitResponse
type WaitResponse struct {
// error
Error *WaitExitError `json:"Error,omitempty"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`
}

@ -0,0 +1,14 @@
package types // import "github.com/docker/docker/api/types"
import "github.com/docker/docker/api/types/volume"
// Volume volume
//
// Deprecated: use github.com/docker/docker/api/types/volume.Volume
type Volume = volume.Volume
// VolumeUsageData Usage details about the volume. This information is used by the
// `GET /system/df` endpoint, and omitted in other endpoints.
//
// Deprecated: use github.com/docker/docker/api/types/volume.UsageData
type VolumeUsageData = volume.UsageData

@ -1,4 +1,5 @@
/*Package filters provides tools for encoding a mapping of keys to a set of /*
Package filters provides tools for encoding a mapping of keys to a set of
multiple values. multiple values.
*/ */
package filters // import "github.com/docker/docker/api/types/filters" package filters // import "github.com/docker/docker/api/types/filters"
@ -9,6 +10,7 @@ import (
"strings" "strings"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/pkg/errors"
) )
// Args stores a mapping of keys to a set of multiple values. // Args stores a mapping of keys to a set of multiple values.
@ -97,7 +99,7 @@ func FromJSON(p string) (Args, error) {
// Fallback to parsing arguments in the legacy slice format // Fallback to parsing arguments in the legacy slice format
deprecated := map[string][]string{} deprecated := map[string][]string{}
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil { if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
return args, err return args, invalidFilter{errors.Wrap(err, "invalid filter")}
} }
args.fields = deprecatedArgs(deprecated) args.fields = deprecatedArgs(deprecated)
@ -247,10 +249,10 @@ func (args Args) Contains(field string) bool {
return ok return ok
} }
type invalidFilter string type invalidFilter struct{ error }
func (e invalidFilter) Error() string { func (e invalidFilter) Error() string {
return "Invalid filter '" + string(e) + "'" return e.error.Error()
} }
func (invalidFilter) InvalidParameter() {} func (invalidFilter) InvalidParameter() {}
@ -260,7 +262,7 @@ func (invalidFilter) InvalidParameter() {}
func (args Args) Validate(accepted map[string]bool) error { func (args Args) Validate(accepted map[string]bool) error {
for name := range args.fields { for name := range args.fields {
if !accepted[name] { if !accepted[name] {
return invalidFilter(name) return invalidFilter{errors.New("invalid filter '" + name + "'")}
} }
} }
return nil return nil

@ -3,15 +3,21 @@ package types
// This file was generated by the swagger tool. // This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command // Editing this file might prove futile when you re-run the swagger generate command
// GraphDriverData Information about a container's graph driver. // GraphDriverData Information about the storage driver used to store the container's and
// image's filesystem.
//
// swagger:model GraphDriverData // swagger:model GraphDriverData
type GraphDriverData struct { type GraphDriverData struct {
// data // Low-level storage metadata, provided as key/value pairs.
//
// This information is driver-specific, and depends on the storage-driver
// in use, and should be used for informational purposes only.
//
// Required: true // Required: true
Data map[string]string `json:"Data"` Data map[string]string `json:"Data"`
// name // Name of the storage driver.
// Required: true // Required: true
Name string `json:"Name"` Name string `json:"Name"`
} }

@ -7,43 +7,91 @@ package types
// swagger:model ImageSummary // swagger:model ImageSummary
type ImageSummary struct { type ImageSummary struct {
// containers // Number of containers using this image. Includes both stopped and running
// containers.
//
// This size is not calculated by default, and depends on which API endpoint
// is used. `-1` indicates that the value has not been set / calculated.
//
// Required: true // Required: true
Containers int64 `json:"Containers"` Containers int64 `json:"Containers"`
// created // Date and time at which the image was created as a Unix timestamp
// (number of seconds sinds EPOCH).
//
// Required: true // Required: true
Created int64 `json:"Created"` Created int64 `json:"Created"`
// Id // ID is the content-addressable ID of an image.
//
// This identifier is a content-addressable digest calculated from the
// image's configuration (which includes the digests of layers used by
// the image).
//
// Note that this digest differs from the `RepoDigests` below, which
// holds digests of image manifests that reference the image.
//
// Required: true // Required: true
ID string `json:"Id"` ID string `json:"Id"`
// labels // User-defined key/value metadata.
// Required: true // Required: true
Labels map[string]string `json:"Labels"` Labels map[string]string `json:"Labels"`
// parent Id // ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is empty if the image was pulled from an image registry.
//
// Required: true // Required: true
ParentID string `json:"ParentId"` ParentID string `json:"ParentId"`
// repo digests // List of content-addressable digests of locally available image manifests
// that the image is referenced from. Multiple manifests can refer to the
// same image.
//
// These digests are usually only available if the image was either pulled
// from a registry, or if the image was pushed to a registry, which is when
// the manifest is generated and its digest calculated.
//
// Required: true // Required: true
RepoDigests []string `json:"RepoDigests"` RepoDigests []string `json:"RepoDigests"`
// repo tags // List of image names/tags in the local image cache that reference this
// image.
//
// Multiple image tags can refer to the same image, and this list may be
// empty if no tags reference the image, in which case the image is
// "untagged", in which case it can still be referenced by its ID.
//
// Required: true // Required: true
RepoTags []string `json:"RepoTags"` RepoTags []string `json:"RepoTags"`
// shared size // Total size of image layers that are shared between this image and other
// images.
//
// This size is not calculated by default. `-1` indicates that the value
// has not been set / calculated.
//
// Required: true // Required: true
SharedSize int64 `json:"SharedSize"` SharedSize int64 `json:"SharedSize"`
// size // Total size of the image including all layers it is composed of.
//
// Required: true // Required: true
Size int64 `json:"Size"` Size int64 `json:"Size"`
// virtual size // Total size of the image including all layers it is composed of.
//
// In versions of Docker before v1.10, this field was calculated from
// the image itself and all of its parent images. Docker v1.10 and up
// store images self-contained, and no longer use a parent-chain, making
// this field an equivalent of the Size field.
//
// This field is kept for backward compatibility, but may be removed in
// a future version of the API.
//
// Required: true // Required: true
VirtualSize int64 `json:"VirtualSize"` VirtualSize int64 `json:"VirtualSize"`
} }

@ -17,6 +17,8 @@ const (
TypeTmpfs Type = "tmpfs" TypeTmpfs Type = "tmpfs"
// TypeNamedPipe is the type for mounting Windows named pipes // TypeNamedPipe is the type for mounting Windows named pipes
TypeNamedPipe Type = "npipe" TypeNamedPipe Type = "npipe"
// TypeCluster is the type for Swarm Cluster Volumes.
TypeCluster = "csi"
) )
// Mount represents a mount (volume). // Mount represents a mount (volume).
@ -30,9 +32,10 @@ type Mount struct {
ReadOnly bool `json:",omitempty"` ReadOnly bool `json:",omitempty"`
Consistency Consistency `json:",omitempty"` Consistency Consistency `json:",omitempty"`
BindOptions *BindOptions `json:",omitempty"` BindOptions *BindOptions `json:",omitempty"`
VolumeOptions *VolumeOptions `json:",omitempty"` VolumeOptions *VolumeOptions `json:",omitempty"`
TmpfsOptions *TmpfsOptions `json:",omitempty"` TmpfsOptions *TmpfsOptions `json:",omitempty"`
ClusterOptions *ClusterOptions `json:",omitempty"`
} }
// Propagation represents the propagation of a mount. // Propagation represents the propagation of a mount.
@ -79,8 +82,9 @@ const (
// BindOptions defines options specific to mounts of type "bind". // BindOptions defines options specific to mounts of type "bind".
type BindOptions struct { type BindOptions struct {
Propagation Propagation `json:",omitempty"` Propagation Propagation `json:",omitempty"`
NonRecursive bool `json:",omitempty"` NonRecursive bool `json:",omitempty"`
CreateMountpoint bool `json:",omitempty"`
} }
// VolumeOptions represents the options for a mount of type volume. // VolumeOptions represents the options for a mount of type volume.
@ -129,3 +133,8 @@ type TmpfsOptions struct {
// Some of these may be straightforward to add, but others, such as // Some of these may be straightforward to add, but others, such as
// uid/gid have implications in a clustered system. // uid/gid have implications in a clustered system.
} }
// ClusterOptions specifies options for a Cluster volume.
type ClusterOptions struct {
// intentionally empty
}

@ -45,31 +45,32 @@ func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
// IndexInfo contains information about a registry // IndexInfo contains information about a registry
// //
// RepositoryInfo Examples: // RepositoryInfo Examples:
// {
// "Index" : {
// "Name" : "docker.io",
// "Mirrors" : ["https://registry-2.docker.io/v1/", "https://registry-3.docker.io/v1/"],
// "Secure" : true,
// "Official" : true,
// },
// "RemoteName" : "library/debian",
// "LocalName" : "debian",
// "CanonicalName" : "docker.io/debian"
// "Official" : true,
// }
// //
// { // {
// "Index" : { // "Index" : {
// "Name" : "127.0.0.1:5000", // "Name" : "docker.io",
// "Mirrors" : [], // "Mirrors" : ["https://registry-2.docker.io/v1/", "https://registry-3.docker.io/v1/"],
// "Secure" : false, // "Secure" : true,
// "Official" : false, // "Official" : true,
// }, // },
// "RemoteName" : "user/repo", // "RemoteName" : "library/debian",
// "LocalName" : "127.0.0.1:5000/user/repo", // "LocalName" : "debian",
// "CanonicalName" : "127.0.0.1:5000/user/repo", // "CanonicalName" : "docker.io/debian"
// "Official" : false, // "Official" : true,
// } // }
//
// {
// "Index" : {
// "Name" : "127.0.0.1:5000",
// "Mirrors" : [],
// "Secure" : false,
// "Official" : false,
// },
// "RemoteName" : "user/repo",
// "LocalName" : "127.0.0.1:5000/user/repo",
// "CanonicalName" : "127.0.0.1:5000/user/repo",
// "Official" : false,
// }
type IndexInfo struct { type IndexInfo struct {
// Name is the name of the registry, such as "docker.io" // Name is the name of the registry, such as "docker.io"
Name string Name string

@ -1,12 +1,20 @@
package swarm // import "github.com/docker/docker/api/types/swarm" package swarm // import "github.com/docker/docker/api/types/swarm"
import "time" import (
"strconv"
"time"
)
// Version represents the internal object version. // Version represents the internal object version.
type Version struct { type Version struct {
Index uint64 `json:",omitempty"` Index uint64 `json:",omitempty"`
} }
// String implements fmt.Stringer interface.
func (v Version) String() string {
return strconv.FormatUint(v.Index, 10)
}
// Meta is a base object inherited by most of the other once. // Meta is a base object inherited by most of the other once.
type Meta struct { type Meta struct {
Version Version `json:",omitempty"` Version Version `json:",omitempty"`

@ -53,6 +53,7 @@ type NodeDescription struct {
Resources Resources `json:",omitempty"` Resources Resources `json:",omitempty"`
Engine EngineDescription `json:",omitempty"` Engine EngineDescription `json:",omitempty"`
TLSInfo TLSInfo `json:",omitempty"` TLSInfo TLSInfo `json:",omitempty"`
CSIInfo []NodeCSIInfo `json:",omitempty"`
} }
// Platform represents the platform (Arch/OS). // Platform represents the platform (Arch/OS).
@ -68,6 +69,21 @@ type EngineDescription struct {
Plugins []PluginDescription `json:",omitempty"` Plugins []PluginDescription `json:",omitempty"`
} }
// NodeCSIInfo represents information about a CSI plugin available on the node
type NodeCSIInfo struct {
// PluginName is the name of the CSI plugin.
PluginName string `json:",omitempty"`
// NodeID is the ID of the node as reported by the CSI plugin. This is
// different from the swarm node ID.
NodeID string `json:",omitempty"`
// MaxVolumesPerNode is the maximum number of volumes that may be published
// to this node
MaxVolumesPerNode int64 `json:",omitempty"`
// AccessibleTopology indicates the location of this node in the CSI
// plugin's topology
AccessibleTopology *Topology `json:",omitempty"`
}
// PluginDescription represents the description of an engine plugin. // PluginDescription represents the description of an engine plugin.
type PluginDescription struct { type PluginDescription struct {
Type string `json:",omitempty"` Type string `json:",omitempty"`
@ -113,3 +129,11 @@ const (
// NodeStateDisconnected DISCONNECTED // NodeStateDisconnected DISCONNECTED
NodeStateDisconnected NodeState = "disconnected" NodeStateDisconnected NodeState = "disconnected"
) )
// Topology defines the CSI topology of this node. This type is a duplicate of
// github.com/docker/docker/api/types.Topology. Because the type definition
// is so simple and to avoid complicated structure or circular imports, we just
// duplicate it here. See that type for full documentation
type Topology struct {
Segments map[string]string `json:",omitempty"`
}

@ -213,6 +213,16 @@ type Info struct {
Warnings []string `json:",omitempty"` Warnings []string `json:",omitempty"`
} }
// Status provides information about the current swarm status and role,
// obtained from the "Swarm" header in the API response.
type Status struct {
// NodeState represents the state of the node.
NodeState LocalNodeState
// ControlAvailable indicates if the node is a swarm manager.
ControlAvailable bool
}
// Peer represents a peer. // Peer represents a peer.
type Peer struct { type Peer struct {
NodeID string NodeID string

@ -62,6 +62,11 @@ type Task struct {
// used to determine which Tasks belong to which run of the job. This field // used to determine which Tasks belong to which run of the job. This field
// is absent if the Service mode is Replicated or Global. // is absent if the Service mode is Replicated or Global.
JobIteration *Version `json:",omitempty"` JobIteration *Version `json:",omitempty"`
// Volumes is the list of VolumeAttachments for this task. It specifies
// which particular volumes are to be used by this particular task, and
// fulfilling what mounts in the spec.
Volumes []VolumeAttachment
} }
// TaskSpec represents the spec of a task. // TaskSpec represents the spec of a task.
@ -204,3 +209,17 @@ type ContainerStatus struct {
type PortStatus struct { type PortStatus struct {
Ports []PortConfig `json:",omitempty"` Ports []PortConfig `json:",omitempty"`
} }
// VolumeAttachment contains the associating a Volume to a Task.
type VolumeAttachment struct {
// ID is the Swarmkit ID of the Volume. This is not the CSI VolumeId.
ID string `json:",omitempty"`
// Source, together with Target, indicates the Mount, as specified in the
// ContainerSpec, that this volume fulfills.
Source string `json:",omitempty"`
// Target, together with Source, indicates the Mount, as specified
// in the ContainerSpec, that this volume fulfills.
Target string `json:",omitempty"`
}

@ -1,12 +0,0 @@
package time // import "github.com/docker/docker/api/types/time"
import (
"strconv"
"time"
)
// DurationToSecondsString converts the specified duration to the number
// seconds it represents, formatted as a string.
func DurationToSecondsString(duration time.Duration) string {
return strconv.FormatFloat(duration.Seconds(), 'f', 0, 64)
}

@ -100,8 +100,10 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
// if the incoming nanosecond portion is longer or shorter than 9 digits it is // if the incoming nanosecond portion is longer or shorter than 9 digits it is
// converted to nanoseconds. The expectation is that the seconds and // converted to nanoseconds. The expectation is that the seconds and
// seconds will be used to create a time variable. For example: // seconds will be used to create a time variable. For example:
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0) //
// if err == nil since := time.Unix(seconds, nanoseconds) // seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
// if err == nil since := time.Unix(seconds, nanoseconds)
//
// returns seconds as def(aultSeconds) if value == "" // returns seconds as def(aultSeconds) if value == ""
func ParseTimestamps(value string, def int64) (int64, int64, error) { func ParseTimestamps(value string, def int64) (int64, int64, error) {
if value == "" { if value == "" {

@ -14,43 +14,136 @@ import (
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/volume"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
) )
const (
// MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams
MediaTypeRawStream = "application/vnd.docker.raw-stream"
// MediaTypeMultiplexedStream is vendor specific MIME-Type set for stdin/stdout/stderr multiplexed streams
MediaTypeMultiplexedStream = "application/vnd.docker.multiplexed-stream"
)
// RootFS returns Image's RootFS description including the layer IDs. // RootFS returns Image's RootFS description including the layer IDs.
type RootFS struct { type RootFS struct {
Type string Type string `json:",omitempty"`
Layers []string `json:",omitempty"` Layers []string `json:",omitempty"`
BaseLayer string `json:",omitempty"`
} }
// ImageInspect contains response of Engine API: // ImageInspect contains response of Engine API:
// GET "/images/{name:.*}/json" // GET "/images/{name:.*}/json"
type ImageInspect struct { type ImageInspect struct {
ID string `json:"Id"` // ID is the content-addressable ID of an image.
RepoTags []string //
RepoDigests []string // This identifier is a content-addressable digest calculated from the
Parent string // image's configuration (which includes the digests of layers used by
Comment string // the image).
Created string //
Container string // Note that this digest differs from the `RepoDigests` below, which
// holds digests of image manifests that reference the image.
ID string `json:"Id"`
// RepoTags is a list of image names/tags in the local image cache that
// reference this image.
//
// Multiple image tags can refer to the same image, and this list may be
// empty if no tags reference the image, in which case the image is
// "untagged", in which case it can still be referenced by its ID.
RepoTags []string
// RepoDigests is a list of content-addressable digests of locally available
// image manifests that the image is referenced from. Multiple manifests can
// refer to the same image.
//
// These digests are usually only available if the image was either pulled
// from a registry, or if the image was pushed to a registry, which is when
// the manifest is generated and its digest calculated.
RepoDigests []string
// Parent is the ID of the parent image.
//
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is empty if the image was pulled from an image registry.
Parent string
// Comment is an optional message that can be set when committing or
// importing the image.
Comment string
// Created is the date and time at which the image was created, formatted in
// RFC 3339 nano-seconds (time.RFC3339Nano).
Created string
// Container is the ID of the container that was used to create the image.
//
// Depending on how the image was created, this field may be empty.
Container string
// ContainerConfig is an optional field containing the configuration of the
// container that was last committed when creating the image.
//
// Previous versions of Docker builder used this field to store build cache,
// and it is not in active use anymore.
ContainerConfig *container.Config ContainerConfig *container.Config
DockerVersion string
Author string // DockerVersion is the version of Docker that was used to build the image.
Config *container.Config //
Architecture string // Depending on how the image was created, this field may be empty.
Variant string `json:",omitempty"` DockerVersion string
Os string
OsVersion string `json:",omitempty"` // Author is the name of the author that was specified when committing the
Size int64 // image, or as specified through MAINTAINER (deprecated) in the Dockerfile.
VirtualSize int64 Author string
GraphDriver GraphDriverData Config *container.Config
RootFS RootFS
Metadata ImageMetadata // Architecture is the hardware CPU architecture that the image runs on.
Architecture string
// Variant is the CPU architecture variant (presently ARM-only).
Variant string `json:",omitempty"`
// OS is the Operating System the image is built to run on.
Os string
// OsVersion is the version of the Operating System the image is built to
// run on (especially for Windows).
OsVersion string `json:",omitempty"`
// Size is the total size of the image including all layers it is composed of.
Size int64
// VirtualSize is the total size of the image including all layers it is
// composed of.
//
// In versions of Docker before v1.10, this field was calculated from
// the image itself and all of its parent images. Docker v1.10 and up
// store images self-contained, and no longer use a parent-chain, making
// this field an equivalent of the Size field.
//
// This field is kept for backward compatibility, but may be removed in
// a future version of the API.
VirtualSize int64 // TODO(thaJeztah): deprecate this field
// GraphDriver holds information about the storage driver used to store the
// container's and image's filesystem.
GraphDriver GraphDriverData
// RootFS contains information about the image's RootFS, including the
// layer IDs.
RootFS RootFS
// Metadata of the image in the local cache.
//
// This information is local to the daemon, and not part of the image itself.
Metadata ImageMetadata
} }
// ImageMetadata contains engine-local data about the image // ImageMetadata contains engine-local data about the image
type ImageMetadata struct { type ImageMetadata struct {
// LastTagTime is the date and time at which the image was last tagged.
LastTagTime time.Time `json:",omitempty"` LastTagTime time.Time `json:",omitempty"`
} }
@ -107,6 +200,15 @@ type Ping struct {
OSType string OSType string
Experimental bool Experimental bool
BuilderVersion BuilderVersion BuilderVersion BuilderVersion
// SwarmStatus provides information about the current swarm status of the
// engine, obtained from the "Swarm" header in the API response.
//
// It can be a nil struct if the API version does not provide this header
// in the ping response, or if an error occurred, in which case the client
// should use other ways to get the current swarm status, such as the /swarm
// endpoint.
SwarmStatus *swarm.Status
} }
// ComponentVersion describes the version information for a specific component. // ComponentVersion describes the version information for a specific component.
@ -158,8 +260,8 @@ type Info struct {
Plugins PluginsInfo Plugins PluginsInfo
MemoryLimit bool MemoryLimit bool
SwapLimit bool SwapLimit bool
KernelMemory bool // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes KernelMemory bool `json:",omitempty"` // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP bool KernelMemoryTCP bool `json:",omitempty"` // KernelMemoryTCP is not supported on cgroups v2.
CPUCfsPeriod bool `json:"CpuCfsPeriod"` CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"` CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool CPUShares bool
@ -288,6 +390,8 @@ type ExecStartCheck struct {
Detach bool Detach bool
// Check if there's a tty // Check if there's a tty
Tty bool Tty bool
// Terminal size [height, width], unused if Tty == false
ConsoleSize *[2]uint `json:",omitempty"`
} }
// HealthcheckResult stores information about a single run of a healthcheck probe // HealthcheckResult stores information about a single run of a healthcheck probe
@ -421,13 +525,44 @@ type DefaultNetworkSettings struct {
// MountPoint represents a mount point configuration inside the container. // MountPoint represents a mount point configuration inside the container.
// This is used for reporting the mountpoints in use by a container. // This is used for reporting the mountpoints in use by a container.
type MountPoint struct { type MountPoint struct {
Type mount.Type `json:",omitempty"` // Type is the type of mount, see `Type<foo>` definitions in
Name string `json:",omitempty"` // github.com/docker/docker/api/types/mount.Type
Source string Type mount.Type `json:",omitempty"`
// Name is the name reference to the underlying data defined by `Source`
// e.g., the volume name.
Name string `json:",omitempty"`
// Source is the source location of the mount.
//
// For volumes, this contains the storage location of the volume (within
// `/var/lib/docker/volumes/`). For bind-mounts, and `npipe`, this contains
// the source (host) part of the bind-mount. For `tmpfs` mount points, this
// field is empty.
Source string
// Destination is the path relative to the container root (`/`) where the
// Source is mounted inside the container.
Destination string Destination string
Driver string `json:",omitempty"`
Mode string // Driver is the volume driver used to create the volume (if it is a volume).
RW bool Driver string `json:",omitempty"`
// Mode is a comma separated list of options supplied by the user when
// creating the bind/volume mount.
//
// The default is platform-specific (`"z"` on Linux, empty on Windows).
Mode string
// RW indicates whether the mount is mounted writable (read-write).
RW bool
// Propagation describes how mounts are propagated from the host into the
// mount point, and vice-versa. Refer to the Linux kernel documentation
// for details:
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
//
// This field is not used on Windows.
Propagation mount.Propagation Propagation mount.Propagation
} }
@ -562,7 +697,7 @@ type DiskUsage struct {
LayersSize int64 LayersSize int64
Images []*ImageSummary Images []*ImageSummary
Containers []*Container Containers []*Container
Volumes []*Volume Volumes []*volume.Volume
BuildCache []*BuildCache BuildCache []*BuildCache
BuilderSize int64 `json:",omitempty"` // Deprecated: deprecated in API 1.38, and no longer used since API 1.40. BuilderSize int64 `json:",omitempty"` // Deprecated: deprecated in API 1.38, and no longer used since API 1.40.
} }

@ -0,0 +1,420 @@
package volume
import (
"github.com/docker/docker/api/types/swarm"
)
// ClusterVolume contains options and information specific to, and only present
// on, Swarm CSI cluster volumes.
type ClusterVolume struct {
// ID is the Swarm ID of the volume. Because cluster volumes are Swarm
// objects, they have an ID, unlike non-cluster volumes, which only have a
// Name. This ID can be used to refer to the cluster volume.
ID string
// Meta is the swarm metadata about this volume.
swarm.Meta
// Spec is the cluster-specific options from which this volume is derived.
Spec ClusterVolumeSpec
// PublishStatus contains the status of the volume as it pertains to its
// publishing on Nodes.
PublishStatus []*PublishStatus `json:",omitempty"`
// Info is information about the global status of the volume.
Info *Info `json:",omitempty"`
}
// ClusterVolumeSpec contains the spec used to create this volume.
type ClusterVolumeSpec struct {
// Group defines the volume group of this volume. Volumes belonging to the
// same group can be referred to by group name when creating Services.
// Referring to a volume by group instructs swarm to treat volumes in that
// group interchangeably for the purpose of scheduling. Volumes with an
// empty string for a group technically all belong to the same, emptystring
// group.
Group string `json:",omitempty"`
// AccessMode defines how the volume is used by tasks.
AccessMode *AccessMode `json:",omitempty"`
// AccessibilityRequirements specifies where in the cluster a volume must
// be accessible from.
//
// This field must be empty if the plugin does not support
// VOLUME_ACCESSIBILITY_CONSTRAINTS capabilities. If it is present but the
// plugin does not support it, volume will not be created.
//
// If AccessibilityRequirements is empty, but the plugin does support
// VOLUME_ACCESSIBILITY_CONSTRAINTS, then Swarmkit will assume the entire
// cluster is a valid target for the volume.
AccessibilityRequirements *TopologyRequirement `json:",omitempty"`
// CapacityRange defines the desired capacity that the volume should be
// created with. If nil, the plugin will decide the capacity.
CapacityRange *CapacityRange `json:",omitempty"`
// Secrets defines Swarm Secrets that are passed to the CSI storage plugin
// when operating on this volume.
Secrets []Secret `json:",omitempty"`
// Availability is the Volume's desired availability. Analogous to Node
// Availability, this allows the user to take volumes offline in order to
// update or delete them.
Availability Availability `json:",omitempty"`
}
// Availability specifies the availability of the volume.
type Availability string
const (
// AvailabilityActive indicates that the volume is active and fully
// schedulable on the cluster.
AvailabilityActive Availability = "active"
// AvailabilityPause indicates that no new workloads should use the
// volume, but existing workloads can continue to use it.
AvailabilityPause Availability = "pause"
// AvailabilityDrain indicates that all workloads using this volume
// should be rescheduled, and the volume unpublished from all nodes.
AvailabilityDrain Availability = "drain"
)
// AccessMode defines the access mode of a volume.
type AccessMode struct {
// Scope defines the set of nodes this volume can be used on at one time.
Scope Scope `json:",omitempty"`
// Sharing defines the number and way that different tasks can use this
// volume at one time.
Sharing SharingMode `json:",omitempty"`
// MountVolume defines options for using this volume as a Mount-type
// volume.
//
// Either BlockVolume or MountVolume, but not both, must be present.
MountVolume *TypeMount `json:",omitempty"`
// BlockVolume defines options for using this volume as a Block-type
// volume.
//
// Either BlockVolume or MountVolume, but not both, must be present.
BlockVolume *TypeBlock `json:",omitempty"`
}
// Scope defines the Scope of a CSI Volume. This is how many nodes a
// Volume can be accessed simultaneously on.
type Scope string
const (
// ScopeSingleNode indicates the volume can be used on one node at a
// time.
ScopeSingleNode Scope = "single"
// ScopeMultiNode indicates the volume can be used on many nodes at
// the same time.
ScopeMultiNode Scope = "multi"
)
// SharingMode defines the Sharing of a CSI Volume. This is how Tasks using a
// Volume at the same time can use it.
type SharingMode string
const (
// SharingNone indicates that only one Task may use the Volume at a
// time.
SharingNone SharingMode = "none"
// SharingReadOnly indicates that the Volume may be shared by any
// number of Tasks, but they must be read-only.
SharingReadOnly SharingMode = "readonly"
// SharingOneWriter indicates that the Volume may be shared by any
// number of Tasks, but all after the first must be read-only.
SharingOneWriter SharingMode = "onewriter"
// SharingAll means that the Volume may be shared by any number of
// Tasks, as readers or writers.
SharingAll SharingMode = "all"
)
// TypeBlock defines options for using a volume as a block-type volume.
//
// Intentionally empty.
type TypeBlock struct{}
// TypeMount contains options for using a volume as a Mount-type
// volume.
type TypeMount struct {
// FsType specifies the filesystem type for the mount volume. Optional.
FsType string `json:",omitempty"`
// MountFlags defines flags to pass when mounting the volume. Optional.
MountFlags []string `json:",omitempty"`
}
// TopologyRequirement expresses the user's requirements for a volume's
// accessible topology.
type TopologyRequirement struct {
// Requisite specifies a list of Topologies, at least one of which the
// volume must be accessible from.
//
// Taken verbatim from the CSI Spec:
//
// Specifies the list of topologies the provisioned volume MUST be
// accessible from.
// This field is OPTIONAL. If TopologyRequirement is specified either
// requisite or preferred or both MUST be specified.
//
// If requisite is specified, the provisioned volume MUST be
// accessible from at least one of the requisite topologies.
//
// Given
// x = number of topologies provisioned volume is accessible from
// n = number of requisite topologies
// The CO MUST ensure n >= 1. The SP MUST ensure x >= 1
// If x==n, then the SP MUST make the provisioned volume available to
// all topologies from the list of requisite topologies. If it is
// unable to do so, the SP MUST fail the CreateVolume call.
// For example, if a volume should be accessible from a single zone,
// and requisite =
// {"region": "R1", "zone": "Z2"}
// then the provisioned volume MUST be accessible from the "region"
// "R1" and the "zone" "Z2".
// Similarly, if a volume should be accessible from two zones, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"}
// then the provisioned volume MUST be accessible from the "region"
// "R1" and both "zone" "Z2" and "zone" "Z3".
//
// If x<n, then the SP SHALL choose x unique topologies from the list
// of requisite topologies. If it is unable to do so, the SP MUST fail
// the CreateVolume call.
// For example, if a volume should be accessible from a single zone,
// and requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"}
// then the SP may choose to make the provisioned volume available in
// either the "zone" "Z2" or the "zone" "Z3" in the "region" "R1".
// Similarly, if a volume should be accessible from two zones, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"},
// {"region": "R1", "zone": "Z4"}
// then the provisioned volume MUST be accessible from any combination
// of two unique topologies: e.g. "R1/Z2" and "R1/Z3", or "R1/Z2" and
// "R1/Z4", or "R1/Z3" and "R1/Z4".
//
// If x>n, then the SP MUST make the provisioned volume available from
// all topologies from the list of requisite topologies and MAY choose
// the remaining x-n unique topologies from the list of all possible
// topologies. If it is unable to do so, the SP MUST fail the
// CreateVolume call.
// For example, if a volume should be accessible from two zones, and
// requisite =
// {"region": "R1", "zone": "Z2"}
// then the provisioned volume MUST be accessible from the "region"
// "R1" and the "zone" "Z2" and the SP may select the second zone
// independently, e.g. "R1/Z4".
Requisite []Topology `json:",omitempty"`
// Preferred is a list of Topologies that the volume should attempt to be
// provisioned in.
//
// Taken from the CSI spec:
//
// Specifies the list of topologies the CO would prefer the volume to
// be provisioned in.
//
// This field is OPTIONAL. If TopologyRequirement is specified either
// requisite or preferred or both MUST be specified.
//
// An SP MUST attempt to make the provisioned volume available using
// the preferred topologies in order from first to last.
//
// If requisite is specified, all topologies in preferred list MUST
// also be present in the list of requisite topologies.
//
// If the SP is unable to to make the provisioned volume available
// from any of the preferred topologies, the SP MAY choose a topology
// from the list of requisite topologies.
// If the list of requisite topologies is not specified, then the SP
// MAY choose from the list of all possible topologies.
// If the list of requisite topologies is specified and the SP is
// unable to to make the provisioned volume available from any of the
// requisite topologies it MUST fail the CreateVolume call.
//
// Example 1:
// Given a volume should be accessible from a single zone, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"}
// preferred =
// {"region": "R1", "zone": "Z3"}
// then the the SP SHOULD first attempt to make the provisioned volume
// available from "zone" "Z3" in the "region" "R1" and fall back to
// "zone" "Z2" in the "region" "R1" if that is not possible.
//
// Example 2:
// Given a volume should be accessible from a single zone, and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"},
// {"region": "R1", "zone": "Z4"},
// {"region": "R1", "zone": "Z5"}
// preferred =
// {"region": "R1", "zone": "Z4"},
// {"region": "R1", "zone": "Z2"}
// then the the SP SHOULD first attempt to make the provisioned volume
// accessible from "zone" "Z4" in the "region" "R1" and fall back to
// "zone" "Z2" in the "region" "R1" if that is not possible. If that
// is not possible, the SP may choose between either the "zone"
// "Z3" or "Z5" in the "region" "R1".
//
// Example 3:
// Given a volume should be accessible from TWO zones (because an
// opaque parameter in CreateVolumeRequest, for example, specifies
// the volume is accessible from two zones, aka synchronously
// replicated), and
// requisite =
// {"region": "R1", "zone": "Z2"},
// {"region": "R1", "zone": "Z3"},
// {"region": "R1", "zone": "Z4"},
// {"region": "R1", "zone": "Z5"}
// preferred =
// {"region": "R1", "zone": "Z5"},
// {"region": "R1", "zone": "Z3"}
// then the the SP SHOULD first attempt to make the provisioned volume
// accessible from the combination of the two "zones" "Z5" and "Z3" in
// the "region" "R1". If that's not possible, it should fall back to
// a combination of "Z5" and other possibilities from the list of
// requisite. If that's not possible, it should fall back to a
// combination of "Z3" and other possibilities from the list of
// requisite. If that's not possible, it should fall back to a
// combination of other possibilities from the list of requisite.
Preferred []Topology `json:",omitempty"`
}
// Topology is a map of topological domains to topological segments.
//
// This description is taken verbatim from the CSI Spec:
//
// A topological domain is a sub-division of a cluster, like "region",
// "zone", "rack", etc.
// A topological segment is a specific instance of a topological domain,
// like "zone3", "rack3", etc.
// For example {"com.company/zone": "Z1", "com.company/rack": "R3"}
// Valid keys have two segments: an OPTIONAL prefix and name, separated
// by a slash (/), for example: "com.company.example/zone".
// The key name segment is REQUIRED. The prefix is OPTIONAL.
// The key name MUST be 63 characters or less, begin and end with an
// alphanumeric character ([a-z0-9A-Z]), and contain only dashes (-),
// underscores (_), dots (.), or alphanumerics in between, for example
// "zone".
// The key prefix MUST be 63 characters or less, begin and end with a
// lower-case alphanumeric character ([a-z0-9]), contain only
// dashes (-), dots (.), or lower-case alphanumerics in between, and
// follow domain name notation format
// (https://tools.ietf.org/html/rfc1035#section-2.3.1).
// The key prefix SHOULD include the plugin's host company name and/or
// the plugin name, to minimize the possibility of collisions with keys
// from other plugins.
// If a key prefix is specified, it MUST be identical across all
// topology keys returned by the SP (across all RPCs).
// Keys MUST be case-insensitive. Meaning the keys "Zone" and "zone"
// MUST not both exist.
// Each value (topological segment) MUST contain 1 or more strings.
// Each string MUST be 63 characters or less and begin and end with an
// alphanumeric character with '-', '_', '.', or alphanumerics in
// between.
type Topology struct {
Segments map[string]string `json:",omitempty"`
}
// CapacityRange describes the minimum and maximum capacity a volume should be
// created with
type CapacityRange struct {
// RequiredBytes specifies that a volume must be at least this big. The
// value of 0 indicates an unspecified minimum.
RequiredBytes int64
// LimitBytes specifies that a volume must not be bigger than this. The
// value of 0 indicates an unspecified maximum
LimitBytes int64
}
// Secret represents a Swarm Secret value that must be passed to the CSI
// storage plugin when operating on this Volume. It represents one key-value
// pair of possibly many.
type Secret struct {
// Key is the name of the key of the key-value pair passed to the plugin.
Key string
// Secret is the swarm Secret object from which to read data. This can be a
// Secret name or ID. The Secret data is retrieved by Swarm and used as the
// value of the key-value pair passed to the plugin.
Secret string
}
// PublishState represents the state of a Volume as it pertains to its
// use on a particular Node.
type PublishState string
const (
// StatePending indicates that the volume should be published on
// this node, but the call to ControllerPublishVolume has not been
// successfully completed yet and the result recorded by swarmkit.
StatePending PublishState = "pending-publish"
// StatePublished means the volume is published successfully to the node.
StatePublished PublishState = "published"
// StatePendingNodeUnpublish indicates that the Volume should be
// unpublished on the Node, and we're waiting for confirmation that it has
// done so. After the Node has confirmed that the Volume has been
// unpublished, the state will move to StatePendingUnpublish.
StatePendingNodeUnpublish PublishState = "pending-node-unpublish"
// StatePendingUnpublish means the volume is still published to the node
// by the controller, awaiting the operation to unpublish it.
StatePendingUnpublish PublishState = "pending-controller-unpublish"
)
// PublishStatus represents the status of the volume as published to an
// individual node
type PublishStatus struct {
// NodeID is the ID of the swarm node this Volume is published to.
NodeID string `json:",omitempty"`
// State is the publish state of the volume.
State PublishState `json:",omitempty"`
// PublishContext is the PublishContext returned by the CSI plugin when
// a volume is published.
PublishContext map[string]string `json:",omitempty"`
}
// Info contains information about the Volume as a whole as provided by
// the CSI storage plugin.
type Info struct {
// CapacityBytes is the capacity of the volume in bytes. A value of 0
// indicates that the capacity is unknown.
CapacityBytes int64 `json:",omitempty"`
// VolumeContext is the context originating from the CSI storage plugin
// when the Volume is created.
VolumeContext map[string]string `json:",omitempty"`
// VolumeID is the ID of the Volume as seen by the CSI storage plugin. This
// is distinct from the Volume's Swarm ID, which is the ID used by all of
// the Docker Engine to refer to the Volume. If this field is blank, then
// the Volume has not been successfully created yet.
VolumeID string `json:",omitempty"`
// AccessibleTopolgoy is the topology this volume is actually accessible
// from.
AccessibleTopology []Topology `json:",omitempty"`
}

@ -0,0 +1,29 @@
package volume
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// CreateOptions VolumeConfig
//
// Volume configuration
// swagger:model CreateOptions
type CreateOptions struct {
// cluster volume spec
ClusterVolumeSpec *ClusterVolumeSpec `json:"ClusterVolumeSpec,omitempty"`
// Name of the volume driver to use.
Driver string `json:"Driver,omitempty"`
// A mapping of driver options and values. These options are
// passed directly to the driver and are driver specific.
//
DriverOpts map[string]string `json:"DriverOpts,omitempty"`
// User-defined key/value metadata.
Labels map[string]string `json:"Labels,omitempty"`
// The new volume's name. If not specified, Docker generates a name.
//
Name string `json:"Name,omitempty"`
}

@ -0,0 +1,11 @@
package volume // import "github.com/docker/docker/api/types/volume"
// VolumeCreateBody Volume configuration
//
// Deprecated: use CreateOptions
type VolumeCreateBody = CreateOptions
// VolumeListOKBody Volume list response
//
// Deprecated: use ListResponse
type VolumeListOKBody = ListResponse

@ -0,0 +1,18 @@
package volume
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// ListResponse VolumeListResponse
//
// Volume list response
// swagger:model ListResponse
type ListResponse struct {
// List of volumes
Volumes []*Volume `json:"Volumes"`
// Warnings that occurred when fetching the list of volumes.
//
Warnings []string `json:"Warnings"`
}

@ -0,0 +1,8 @@
package volume // import "github.com/docker/docker/api/types/volume"
import "github.com/docker/docker/api/types/filters"
// ListOptions holds parameters to list volumes.
type ListOptions struct {
Filters filters.Args
}

@ -1,4 +1,4 @@
package types package volume
// This file was generated by the swagger tool. // This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command // Editing this file might prove futile when you re-run the swagger generate command
@ -7,6 +7,9 @@ package types
// swagger:model Volume // swagger:model Volume
type Volume struct { type Volume struct {
// cluster volume
ClusterVolume *ClusterVolume `json:"ClusterVolume,omitempty"`
// Date/Time the volume was created. // Date/Time the volume was created.
CreatedAt string `json:"CreatedAt,omitempty"` CreatedAt string `json:"CreatedAt,omitempty"`
@ -47,14 +50,14 @@ type Volume struct {
Status map[string]interface{} `json:"Status,omitempty"` Status map[string]interface{} `json:"Status,omitempty"`
// usage data // usage data
UsageData *VolumeUsageData `json:"UsageData,omitempty"` UsageData *UsageData `json:"UsageData,omitempty"`
} }
// VolumeUsageData Usage details about the volume. This information is used by the // UsageData Usage details about the volume. This information is used by the
// `GET /system/df` endpoint, and omitted in other endpoints. // `GET /system/df` endpoint, and omitted in other endpoints.
// //
// swagger:model VolumeUsageData // swagger:model UsageData
type VolumeUsageData struct { type UsageData struct {
// The number of containers referencing this volume. This field // The number of containers referencing this volume. This field
// is set to `-1` if the reference-count is not available. // is set to `-1` if the reference-count is not available.

@ -1,31 +0,0 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// VolumeCreateBody Volume configuration
// swagger:model VolumeCreateBody
type VolumeCreateBody struct {
// Name of the volume driver to use.
// Required: true
Driver string `json:"Driver"`
// A mapping of driver options and values. These options are
// passed directly to the driver and are driver specific.
//
// Required: true
DriverOpts map[string]string `json:"DriverOpts"`
// User-defined key/value metadata.
// Required: true
Labels map[string]string `json:"Labels"`
// The new volume's name. If not specified, Docker generates a name.
//
// Required: true
Name string `json:"Name"`
}

@ -1,23 +0,0 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
import "github.com/docker/docker/api/types"
// VolumeListOKBody Volume list response
// swagger:model VolumeListOKBody
type VolumeListOKBody struct {
// List of volumes
// Required: true
Volumes []*types.Volume `json:"Volumes"`
// Warnings that occurred when fetching the list of volumes.
//
// Required: true
Warnings []string `json:"Warnings"`
}

@ -0,0 +1,7 @@
package volume // import "github.com/docker/docker/api/types/volume"
// UpdateOptions is configuration to update a Volume with.
type UpdateOptions struct {
// Spec is the ClusterVolumeSpec to update the volume to.
Spec *ClusterVolumeSpec `json:"Spec,omitempty"`
}

@ -0,0 +1,86 @@
// Package urlutil provides helper function to check if a given build-context
// location should be considered a URL or a remote Git repository.
//
// This package is specifically written for use with docker build contexts, and
// should not be used as a general-purpose utility.
package urlutil // import "github.com/docker/docker/builder/remotecontext/urlutil"
import (
"regexp"
"strings"
)
// urlPathWithFragmentSuffix matches fragments to use as Git reference and build
// context from the Git repository. See IsGitURL for details.
var urlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$")
// IsURL returns true if the provided str is an HTTP(S) URL by checking if it
// has a http:// or https:// scheme. No validation is performed to verify if the
// URL is well-formed.
func IsURL(str string) bool {
return strings.HasPrefix(str, "https://") || strings.HasPrefix(str, "http://")
}
// IsGitURL returns true if the provided str is a remote git repository "URL".
//
// This function only performs a rudimentary check (no validation is performed
// to ensure the URL is well-formed), and is written specifically for use with
// docker build, with some logic for backward compatibility with older versions
// of docker: do not use this function as a general-purpose utility.
//
// The following patterns are considered to be a Git URL:
//
// - https://(.*).git(?:#.+)?$ git repository URL with optional fragment, as known to be used by GitHub and GitLab.
// - http://(.*).git(?:#.+)?$ same, but non-TLS
// - git://(.*) URLs using git:// scheme
// - git@(.*)
// - github.com/ see description below
//
// The github.com/ prefix is a special case used to treat context-paths
// starting with "github.com/" as a git URL if the given path does not
// exist locally. The "github.com/" prefix is kept for backward compatibility,
// and is a legacy feature.
//
// Going forward, no additional prefixes should be added, and users should
// be encouraged to use explicit URLs (https://github.com/user/repo.git) instead.
//
// Note that IsGitURL does not check if "github.com/" prefixes exist as a local
// path. Code using this function should check if the path exists locally before
// using it as a URL.
//
// # Fragments
//
// Git URLs accept context configuration in their fragment section, separated by
// a colon (`:`). The first part represents the reference to check out, and can
// be either a branch, a tag, or a remote reference. The second part represents
// a subdirectory inside the repository to use as the build context.
//
// For example,the following URL uses a directory named "docker" in the branch
// "container" in the https://github.com/myorg/my-repo.git repository:
//
// https://github.com/myorg/my-repo.git#container:docker
//
// The following table represents all the valid suffixes with their build
// contexts:
//
// | Build Syntax Suffix | Git reference used | Build Context Used |
// |--------------------------------|----------------------|--------------------|
// | my-repo.git | refs/heads/master | / |
// | my-repo.git#mytag | refs/tags/my-tag | / |
// | my-repo.git#mybranch | refs/heads/my-branch | / |
// | my-repo.git#pull/42/head | refs/pull/42/head | / |
// | my-repo.git#:directory | refs/heads/master | /directory |
// | my-repo.git#master:directory | refs/heads/master | /directory |
// | my-repo.git#mytag:directory | refs/tags/my-tag | /directory |
// | my-repo.git#mybranch:directory | refs/heads/my-branch | /directory |
func IsGitURL(str string) bool {
if IsURL(str) && urlPathWithFragmentSuffix.MatchString(str) {
return true
}
for _, prefix := range []string{"git://", "github.com/", "git@"} {
if strings.HasPrefix(str, prefix) {
return true
}
}
return false
}

@ -20,7 +20,7 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil) resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return checkpoints, wrapResponseError(err, resp, "container", container) return checkpoints, err
} }
err = json.NewDecoder(resp.body).Decode(&checkpoints) err = json.NewDecoder(resp.body).Decode(&checkpoints)

@ -4,7 +4,7 @@ Package client is a Go client for the Docker Engine API.
For more information about the Engine API, see the documentation: For more information about the Engine API, see the documentation:
https://docs.docker.com/engine/api/ https://docs.docker.com/engine/api/
Usage # Usage
You use the library by creating a client object and calling methods on it. The You use the library by creating a client object and calling methods on it. The
client can be created either from environment variables with NewClientWithOpts(client.FromEnv), client can be created either from environment variables with NewClientWithOpts(client.FromEnv),
@ -37,13 +37,11 @@ For example, to list running containers (the equivalent of "docker ps"):
fmt.Printf("%s %s\n", container.ID[:10], container.Image) fmt.Printf("%s %s\n", container.ID[:10], container.Image)
} }
} }
*/ */
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"fmt"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -93,15 +91,18 @@ type Client struct {
} }
// CheckRedirect specifies the policy for dealing with redirect responses: // CheckRedirect specifies the policy for dealing with redirect responses:
// If the request is non-GET return `ErrRedirect`. Otherwise use the last response. // If the request is non-GET return ErrRedirect, otherwise use the last response.
//
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308)
// in the client. The Docker client (and by extension docker API client) can be
// made to send a request like POST /containers//start where what would normally
// be in the name section of the URL is empty. This triggers an HTTP 301 from
// the daemon.
// //
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client . // In go 1.8 this 301 will be converted to a GET request, and ends up getting
// The Docker client (and by extension docker API client) can be made to send a request // a 404 from the daemon. This behavior change manifests in the client in that
// like POST /containers//start where what would normally be in the name section of the URL is empty. // before, the 301 was not followed and the client did not generate an error,
// This triggers an HTTP 301 from the daemon. // but now results in a message like Error response from daemon: page not found.
// In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon.
// This behavior change manifests in the client in that before the 301 was not followed and
// the client did not generate an error, but now results in a message like Error response from daemon: page not found.
func CheckRedirect(req *http.Request, via []*http.Request) error { func CheckRedirect(req *http.Request, via []*http.Request) error {
if via[0].Method == http.MethodGet { if via[0].Method == http.MethodGet {
return http.ErrUseLastResponse return http.ErrUseLastResponse
@ -109,13 +110,20 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
return ErrRedirect return ErrRedirect
} }
// NewClientWithOpts initializes a new API client with default values. It takes functors // NewClientWithOpts initializes a new API client with a default HTTPClient, and
// to modify values when creating it, like `NewClientWithOpts(WithVersion(…))` // default API host and version. It also initializes the custom HTTP headers to
// It also initializes the custom http headers to add to each request. // add to each request.
// //
// It won't send any version information if the version number is empty. It is // It takes an optional list of Opt functional arguments, which are applied in
// highly recommended that you set a version or your client may break if the // the order they're provided, which allows modifying the defaults when creating
// server is upgraded. // the client. For example, the following initializes a client that configures
// itself with values from environment variables (client.FromEnv), and has
// automatic API version negotiation enabled (client.WithAPIVersionNegotiation()).
//
// cli, err := client.NewClientWithOpts(
// client.FromEnv,
// client.WithAPIVersionNegotiation(),
// )
func NewClientWithOpts(ops ...Opt) (*Client, error) { func NewClientWithOpts(ops ...Opt) (*Client, error) {
client, err := defaultHTTPClient(DefaultDockerHost) client, err := defaultHTTPClient(DefaultDockerHost)
if err != nil { if err != nil {
@ -153,12 +161,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
} }
func defaultHTTPClient(host string) (*http.Client, error) { func defaultHTTPClient(host string) (*http.Client, error) {
url, err := ParseHostURL(host) hostURL, err := ParseHostURL(host)
if err != nil { if err != nil {
return nil, err return nil, err
} }
transport := new(http.Transport) transport := &http.Transport{}
sockets.ConfigureTransport(transport, url.Scheme, url.Host) _ = sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
return &http.Client{ return &http.Client{
Transport: transport, Transport: transport,
CheckRedirect: CheckRedirect, CheckRedirect: CheckRedirect,
@ -194,11 +202,21 @@ func (cli *Client) ClientVersion() string {
return cli.version return cli.version
} }
// NegotiateAPIVersion queries the API and updates the version to match the // NegotiateAPIVersion queries the API and updates the version to match the API
// API version. Any errors are silently ignored. If a manual override is in place, // version. NegotiateAPIVersion downgrades the client's API version to match the
// either through the `DOCKER_API_VERSION` environment variable, or if the client // APIVersion if the ping version is lower than the default version. If the API
// was initialized with a fixed version (`opts.WithVersion(xx)`), no negotiation // version reported by the server is higher than the maximum version supported
// will be performed. // by the client, it uses the client's maximum version.
//
// If a manual override is in place, either through the "DOCKER_API_VERSION"
// (EnvOverrideAPIVersion) environment variable, or if the client is initialized
// with a fixed version (WithVersion(xx)), no negotiation is performed.
//
// If the API server's ping response does not contain an API version, or if the
// client did not get a successful ping response, it assumes it is connected with
// an old daemon that does not support API version negotiation, in which case it
// downgrades to the latest version of the API before version negotiation was
// added (1.24).
func (cli *Client) NegotiateAPIVersion(ctx context.Context) { func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
if !cli.manualOverride { if !cli.manualOverride {
ping, _ := cli.Ping(ctx) ping, _ := cli.Ping(ctx)
@ -206,23 +224,31 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
} }
} }
// NegotiateAPIVersionPing updates the client version to match the Ping.APIVersion // NegotiateAPIVersionPing downgrades the client's API version to match the
// if the ping version is less than the default version. If a manual override is // APIVersion in the ping response. If the API version in pingResponse is higher
// in place, either through the `DOCKER_API_VERSION` environment variable, or if // than the maximum version supported by the client, it uses the client's maximum
// the client was initialized with a fixed version (`opts.WithVersion(xx)`), no // version.
// negotiation is performed. //
func (cli *Client) NegotiateAPIVersionPing(p types.Ping) { // If a manual override is in place, either through the "DOCKER_API_VERSION"
// (EnvOverrideAPIVersion) environment variable, or if the client is initialized
// with a fixed version (WithVersion(xx)), no negotiation is performed.
//
// If the API server's ping response does not contain an API version, we assume
// we are connected with an old daemon without API version negotiation support,
// and downgrade to the latest version of the API before version negotiation was
// added (1.24).
func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) {
if !cli.manualOverride { if !cli.manualOverride {
cli.negotiateAPIVersionPing(p) cli.negotiateAPIVersionPing(pingResponse)
} }
} }
// negotiateAPIVersionPing queries the API and updates the version to match the // negotiateAPIVersionPing queries the API and updates the version to match the
// API version. Any errors are silently ignored. // API version from the ping response.
func (cli *Client) negotiateAPIVersionPing(p types.Ping) { func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) {
// try the latest version before versioning headers existed // default to the latest version before versioning headers existed
if p.APIVersion == "" { if pingResponse.APIVersion == "" {
p.APIVersion = "1.24" pingResponse.APIVersion = "1.24"
} }
// if the client is not initialized with a version, start with the latest supported version // if the client is not initialized with a version, start with the latest supported version
@ -231,8 +257,8 @@ func (cli *Client) negotiateAPIVersionPing(p types.Ping) {
} }
// if server version is lower than the client version, downgrade // if server version is lower than the client version, downgrade
if versions.LessThan(p.APIVersion, cli.version) { if versions.LessThan(pingResponse.APIVersion, cli.version) {
cli.version = p.APIVersion cli.version = pingResponse.APIVersion
} }
// Store the results, so that automatic API version negotiation (if enabled) // Store the results, so that automatic API version negotiation (if enabled)
@ -258,7 +284,7 @@ func (cli *Client) HTTPClient() *http.Client {
func ParseHostURL(host string) (*url.URL, error) { func ParseHostURL(host string) (*url.URL, error) {
protoAddrParts := strings.SplitN(host, "://", 2) protoAddrParts := strings.SplitN(host, "://", 2)
if len(protoAddrParts) == 1 { if len(protoAddrParts) == 1 {
return nil, fmt.Errorf("unable to parse docker host `%s`", host) return nil, errors.Errorf("unable to parse docker host `%s`", host)
} }
var basePath string var basePath string
@ -278,7 +304,9 @@ func ParseHostURL(host string) (*url.URL, error) {
}, nil }, nil
} }
// Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection. // Dialer returns a dialer for a raw stream connection, with an HTTP/1.1 header,
// that can be used for proxying the daemon connection.
//
// Used by `docker dial-stdio` (docker/cli#889). // Used by `docker dial-stdio` (docker/cli#889).
func (cli *Client) Dialer() func(context.Context) (net.Conn, error) { func (cli *Client) Dialer() func(context.Context) (net.Conn, error) {
return func(ctx context.Context) (net.Conn, error) { return func(ctx context.Context) (net.Conn, error) {

@ -3,7 +3,8 @@
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset // DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "unix:///var/run/docker.sock" const DefaultDockerHost = "unix:///var/run/docker.sock"
const defaultProto = "unix" const defaultProto = "unix"

@ -1,6 +1,7 @@
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset // DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "npipe:////./pipe/docker_engine" const DefaultDockerHost = "npipe:////./pipe/docker_engine"
const defaultProto = "npipe" const defaultProto = "npipe"

@ -20,7 +20,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
resp, err := cli.get(ctx, "/configs/"+id, nil, nil) resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id) return swarm.Config{}, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.body)

@ -9,5 +9,5 @@ func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
} }
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil) resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "config", id) return err
} }

@ -3,7 +3,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"net/url" "net/url"
"strconv"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
) )
@ -14,7 +13,7 @@ func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Ve
return err return err
} }
query := url.Values{} query := url.Values{}
query.Set("version", strconv.FormatUint(version.Index, 10)) query.Set("version", version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, config, nil) resp, err := cli.post(ctx, "/configs/"+id+"/update", query, config, nil)
ensureReaderClosed(resp) ensureReaderClosed(resp)
return err return err

@ -22,7 +22,7 @@ import (
// multiplexed. // multiplexed.
// The format of the multiplexed stream is as follows: // The format of the multiplexed stream is as follows:
// //
// [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT} // [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT}
// //
// STREAM_TYPE can be 1 for stdout and 2 for stderr // STREAM_TYPE can be 1 for stdout and 2 for stderr
// //
@ -52,6 +52,8 @@ func (cli *Client) ContainerAttach(ctx context.Context, container string, option
query.Set("logs", "1") query.Set("logs", "1")
} }
headers := map[string][]string{"Content-Type": {"text/plain"}} headers := map[string][]string{
"Content-Type": {"text/plain"},
}
return cli.postHijacked(ctx, "/containers/"+container+"/attach", query, nil, headers) return cli.postHijacked(ctx, "/containers/"+container+"/attach", query, nil, headers)
} }

@ -23,7 +23,7 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
response, err := cli.head(ctx, urlStr, query, nil) response, err := cli.head(ctx, urlStr, query, nil)
defer ensureReaderClosed(response) defer ensureReaderClosed(response)
if err != nil { if err != nil {
return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path) return types.ContainerPathStat{}, err
} }
return getContainerPathStatFromHeader(response.header) return getContainerPathStatFromHeader(response.header)
} }
@ -47,12 +47,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
response, err := cli.putRaw(ctx, apiPath, query, content, nil) response, err := cli.putRaw(ctx, apiPath, query, content, nil)
defer ensureReaderClosed(response) defer ensureReaderClosed(response)
if err != nil { if err != nil {
return wrapResponseError(err, response, "container:path", containerID+":"+dstPath) return err
}
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
if response.statusCode != http.StatusOK {
return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
} }
return nil return nil
@ -67,12 +62,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
apiPath := "/containers/" + containerID + "/archive" apiPath := "/containers/" + containerID + "/archive"
response, err := cli.get(ctx, apiPath, query, nil) response, err := cli.get(ctx, apiPath, query, nil)
if err != nil { if err != nil {
return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath) return nil, types.ContainerPathStat{}, err
}
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
if response.statusCode != http.StatusOK {
return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
} }
// In order to get the copy behavior right, we need to know information // In order to get the copy behavior right, we need to know information

@ -20,18 +20,25 @@ type configWrapper struct {
// ContainerCreate creates a new container based on the given configuration. // ContainerCreate creates a new container based on the given configuration.
// It can be associated with a name, but it's not mandatory. // It can be associated with a name, but it's not mandatory.
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) { func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.CreateResponse, error) {
var response container.ContainerCreateCreatedBody var response container.CreateResponse
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
return response, err return response, err
} }
clientVersion := cli.ClientVersion()
// When using API 1.24 and under, the client is responsible for removing the container // When using API 1.24 and under, the client is responsible for removing the container
if hostConfig != nil && versions.LessThan(cli.ClientVersion(), "1.25") { if hostConfig != nil && versions.LessThan(clientVersion, "1.25") {
hostConfig.AutoRemove = false hostConfig.AutoRemove = false
} }
// When using API under 1.42, the Linux daemon doesn't respect the ConsoleSize
if hostConfig != nil && platform != nil && platform.OS == "linux" && versions.LessThan(clientVersion, "1.42") {
hostConfig.ConsoleSize = [2]uint{0, 0}
}
if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil { if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil {
return response, err return response, err
} }

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
) )
// ContainerExecCreate creates a new exec configuration to run an exec process. // ContainerExecCreate creates a new exec configuration to run an exec process.
@ -14,6 +15,9 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co
if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil { if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil {
return response, err return response, err
} }
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}
resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil) resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
@ -26,6 +30,9 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co
// ContainerExecStart starts an exec process already created in the docker host. // ContainerExecStart starts an exec process already created in the docker host.
func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error { func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}
resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil) resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil)
ensureReaderClosed(resp) ensureReaderClosed(resp)
return err return err
@ -36,7 +43,12 @@ func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config
// and the a reader to get output. It's up to the called to close // and the a reader to get output. It's up to the called to close
// the hijacked connection by calling types.HijackedResponse.Close. // the hijacked connection by calling types.HijackedResponse.Close.
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) { func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) {
headers := map[string][]string{"Content-Type": {"application/json"}} if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}
headers := map[string][]string{
"Content-Type": {"application/json"},
}
return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, config, headers) return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, config, headers)
} }

@ -18,7 +18,7 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, err
} }
var response types.ContainerJSON var response types.ContainerJSON
@ -38,7 +38,7 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

@ -8,7 +8,9 @@ import (
// ContainerKill terminates the container process but does not remove the container from the docker host. // ContainerKill terminates the container process but does not remove the container from the docker host.
func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error { func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error {
query := url.Values{} query := url.Values{}
query.Set("signal", signal) if signal != "" {
query.Set("signal", signal)
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/kill", query, nil, nil) resp, err := cli.post(ctx, "/containers/"+containerID+"/kill", query, nil, nil)
ensureReaderClosed(resp) ensureReaderClosed(resp)

@ -18,7 +18,7 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis
query.Set("all", "1") query.Set("all", "1")
} }
if options.Limit != -1 { if options.Limit > 0 {
query.Set("limit", strconv.Itoa(options.Limit)) query.Set("limit", strconv.Itoa(options.Limit))
} }

@ -24,7 +24,7 @@ import (
// multiplexed. // multiplexed.
// The format of the multiplexed stream is as follows: // The format of the multiplexed stream is as follows:
// //
// [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT} // [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT}
// //
// STREAM_TYPE can be 1 for stdout and 2 for stderr // STREAM_TYPE can be 1 for stdout and 2 for stderr
// //
@ -74,7 +74,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil) resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil)
if err != nil { if err != nil {
return nil, wrapResponseError(err, resp, "container", container) return nil, err
} }
return resp.body, nil return resp.body, nil
} }

@ -23,5 +23,5 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil) resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "container", containerID) return err
} }

@ -3,18 +3,22 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"net/url" "net/url"
"time" "strconv"
timetypes "github.com/docker/docker/api/types/time" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
) )
// ContainerRestart stops and starts a container again. // ContainerRestart stops and starts a container again.
// It makes the daemon wait for the container to be up again for // It makes the daemon wait for the container to be up again for
// a specific amount of time, given the timeout. // a specific amount of time, given the timeout.
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error { func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
query := url.Values{} query := url.Values{}
if timeout != nil { if options.Timeout != nil {
query.Set("t", timetypes.DurationToSecondsString(*timeout)) query.Set("t", strconv.Itoa(*options.Timeout))
}
if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
query.Set("signal", options.Signal)
} }
resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil) resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
ensureReaderClosed(resp) ensureReaderClosed(resp)

@ -3,9 +3,10 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"net/url" "net/url"
"time" "strconv"
timetypes "github.com/docker/docker/api/types/time" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
) )
// ContainerStop stops a container. In case the container fails to stop // ContainerStop stops a container. In case the container fails to stop
@ -15,10 +16,13 @@ import (
// If the timeout is nil, the container's StopTimeout value is used, if set, // If the timeout is nil, the container's StopTimeout value is used, if set,
// otherwise the engine default. A negative timeout value can be specified, // otherwise the engine default. A negative timeout value can be specified,
// meaning no timeout, i.e. no forceful termination is performed. // meaning no timeout, i.e. no forceful termination is performed.
func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error { func (cli *Client) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
query := url.Values{} query := url.Values{}
if timeout != nil { if options.Timeout != nil {
query.Set("t", timetypes.DurationToSecondsString(*timeout)) query.Set("t", strconv.Itoa(*options.Timeout))
}
if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
query.Set("signal", options.Signal)
} }
resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil) resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
ensureReaderClosed(resp) ensureReaderClosed(resp)

@ -24,12 +24,12 @@ import (
// wait request or in getting the response. This allows the caller to // wait request or in getting the response. This allows the caller to
// synchronize ContainerWait with other calls, such as specifying a // synchronize ContainerWait with other calls, such as specifying a
// "next-exit" condition before issuing a ContainerStart request. // "next-exit" condition before issuing a ContainerStart request.
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) { func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
if versions.LessThan(cli.ClientVersion(), "1.30") { if versions.LessThan(cli.ClientVersion(), "1.30") {
return cli.legacyContainerWait(ctx, containerID) return cli.legacyContainerWait(ctx, containerID)
} }
resultC := make(chan container.ContainerWaitOKBody) resultC := make(chan container.WaitResponse)
errC := make(chan error, 1) errC := make(chan error, 1)
query := url.Values{} query := url.Values{}
@ -46,7 +46,7 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
go func() { go func() {
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
var res container.ContainerWaitOKBody var res container.WaitResponse
if err := json.NewDecoder(resp.body).Decode(&res); err != nil { if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
errC <- err errC <- err
return return
@ -60,8 +60,8 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
// legacyContainerWait returns immediately and doesn't have an option to wait // legacyContainerWait returns immediately and doesn't have an option to wait
// until the container is removed. // until the container is removed.
func (cli *Client) legacyContainerWait(ctx context.Context, containerID string) (<-chan container.ContainerWaitOKBody, <-chan error) { func (cli *Client) legacyContainerWait(ctx context.Context, containerID string) (<-chan container.WaitResponse, <-chan error) {
resultC := make(chan container.ContainerWaitOKBody) resultC := make(chan container.WaitResponse)
errC := make(chan error) errC := make(chan error)
go func() { go func() {
@ -72,7 +72,7 @@ func (cli *Client) legacyContainerWait(ctx context.Context, containerID string)
} }
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
var res container.ContainerWaitOKBody var res container.WaitResponse
if err := json.NewDecoder(resp.body).Decode(&res); err != nil { if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
errC <- err errC <- err
return return

@ -0,0 +1,90 @@
package client // import "github.com/docker/docker/client"
const (
// EnvOverrideHost is the name of the environment variable that can be used
// to override the default host to connect to (DefaultDockerHost).
//
// This env-var is read by FromEnv and WithHostFromEnv and when set to a
// non-empty value, takes precedence over the default host (which is platform
// specific), or any host already set.
EnvOverrideHost = "DOCKER_HOST"
// EnvOverrideAPIVersion is the name of the environment variable that can
// be used to override the API version to use. Value should be
// formatted as MAJOR.MINOR, for example, "1.19".
//
// This env-var is read by FromEnv and WithVersionFromEnv and when set to a
// non-empty value, takes precedence over API version negotiation.
//
// This environment variable should be used for debugging purposes only, as
// it can set the client to use an incompatible (or invalid) API version.
EnvOverrideAPIVersion = "DOCKER_API_VERSION"
// EnvOverrideCertPath is the name of the environment variable that can be
// used to specify the directory from which to load the TLS certificates
// (ca.pem, cert.pem, key.pem) from. These certificates are used to configure
// the Client for a TCP connection protected by TLS client authentication.
//
// TLS certificate verification is enabled by default if the Client is configured
// to use a TLS connection. Refer to EnvTLSVerify below to learn how to
// disable verification for testing purposes.
//
// WARNING: Access to the remote API is equivalent to root access to the
// host where the daemon runs. Do not expose the API without protection,
// and only if needed. Make sure you are familiar with the "daemon attack
// surface" (https://docs.docker.com/go/attack-surface/).
//
// For local access to the API, it is recommended to connect with the daemon
// using the default local socket connection (on Linux), or the named pipe
// (on Windows).
//
// If you need to access the API of a remote daemon, consider using an SSH
// (ssh://) connection, which is easier to set up, and requires no additional
// configuration if the host is accessible using ssh.
//
// If you cannot use the alternatives above, and you must expose the API over
// a TCP connection, refer to https://docs.docker.com/engine/security/protect-access/
// to learn how to configure the daemon and client to use a TCP connection
// with TLS client authentication. Make sure you know the differences between
// a regular TLS connection and a TLS connection protected by TLS client
// authentication, and verify that the API cannot be accessed by other clients.
EnvOverrideCertPath = "DOCKER_CERT_PATH"
// EnvTLSVerify is the name of the environment variable that can be used to
// enable or disable TLS certificate verification. When set to a non-empty
// value, TLS certificate verification is enabled, and the client is configured
// to use a TLS connection, using certificates from the default directories
// (within `~/.docker`); refer to EnvOverrideCertPath above for additional
// details.
//
// WARNING: Access to the remote API is equivalent to root access to the
// host where the daemon runs. Do not expose the API without protection,
// and only if needed. Make sure you are familiar with the "daemon attack
// surface" (https://docs.docker.com/go/attack-surface/).
//
// Before setting up your client and daemon to use a TCP connection with TLS
// client authentication, consider using one of the alternatives mentioned
// in EnvOverrideCertPath above.
//
// Disabling TLS certificate verification (for testing purposes)
//
// TLS certificate verification is enabled by default if the Client is configured
// to use a TLS connection, and it is highly recommended to keep verification
// enabled to prevent machine-in-the-middle attacks. Refer to the documentation
// at https://docs.docker.com/engine/security/protect-access/ and pages linked
// from that page to learn how to configure the daemon and client to use a
// TCP connection with TLS client authentication enabled.
//
// Set the "DOCKER_TLS_VERIFY" environment to an empty string ("") to
// disable TLS certificate verification. Disabling verification is insecure,
// so should only be done for testing purposes. From the Go documentation
// (https://pkg.go.dev/crypto/tls#Config):
//
// InsecureSkipVerify controls whether a client verifies the server's
// certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
// accepts any certificate presented by the server and any host name in that
// certificate. In this mode, TLS is susceptible to machine-in-the-middle
// attacks unless custom verification is used. This should be used only for
// testing or in combination with VerifyConnection or VerifyPeerCertificate.
EnvTLSVerify = "DOCKER_TLS_VERIFY"
)

@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"fmt" "fmt"
"net/http"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
@ -41,11 +40,11 @@ type notFound interface {
// IsErrNotFound returns true if the error is a NotFound error, which is returned // IsErrNotFound returns true if the error is a NotFound error, which is returned
// by the API when some object is not found. // by the API when some object is not found.
func IsErrNotFound(err error) bool { func IsErrNotFound(err error) bool {
var e notFound if errdefs.IsNotFound(err) {
if errors.As(err, &e) {
return true return true
} }
return errdefs.IsNotFound(err) var e notFound
return errors.As(err, &e)
} }
type objectNotFoundError struct { type objectNotFoundError struct {
@ -59,35 +58,11 @@ func (e objectNotFoundError) Error() string {
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id) return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
} }
func wrapResponseError(err error, resp serverResponse, object, id string) error {
switch {
case err == nil:
return nil
case resp.statusCode == http.StatusNotFound:
return objectNotFoundError{object: object, id: id}
case resp.statusCode == http.StatusNotImplemented:
return errdefs.NotImplemented(err)
default:
return err
}
}
// unauthorizedError represents an authorization error in a remote registry.
type unauthorizedError struct {
cause error
}
// Error returns a string representation of an unauthorizedError
func (u unauthorizedError) Error() string {
return u.cause.Error()
}
// IsErrUnauthorized returns true if the error is caused // IsErrUnauthorized returns true if the error is caused
// when a remote registry authentication fails // when a remote registry authentication fails
//
// Deprecated: use errdefs.IsUnauthorized
func IsErrUnauthorized(err error) bool { func IsErrUnauthorized(err error) bool {
if _, ok := err.(unauthorizedError); ok {
return ok
}
return errdefs.IsUnauthorized(err) return errdefs.IsUnauthorized(err)
} }
@ -99,32 +74,12 @@ func (e pluginPermissionDenied) Error() string {
return "Permission denied while installing plugin " + e.name return "Permission denied while installing plugin " + e.name
} }
// IsErrPluginPermissionDenied returns true if the error is caused
// when a user denies a plugin's permissions
func IsErrPluginPermissionDenied(err error) bool {
_, ok := err.(pluginPermissionDenied)
return ok
}
type notImplementedError struct {
message string
}
func (e notImplementedError) Error() string {
return e.message
}
func (e notImplementedError) NotImplemented() bool {
return true
}
// IsErrNotImplemented returns true if the error is a NotImplemented error. // IsErrNotImplemented returns true if the error is a NotImplemented error.
// This is returned by the API when a requested feature has not been // This is returned by the API when a requested feature has not been
// implemented. // implemented.
//
// Deprecated: use errdefs.IsNotImplemented
func IsErrNotImplemented(err error) bool { func IsErrNotImplemented(err error) bool {
if _, ok := err.(notImplementedError); ok {
return ok
}
return errdefs.IsNotImplemented(err) return errdefs.IsNotImplemented(err)
} }

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -30,12 +31,12 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
} }
req = cli.addHeaders(req, headers) req = cli.addHeaders(req, headers)
conn, err := cli.setupHijackConn(ctx, req, "tcp") conn, mediaType, err := cli.setupHijackConn(ctx, req, "tcp")
if err != nil { if err != nil {
return types.HijackedResponse{}, err return types.HijackedResponse{}, err
} }
return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err return types.NewHijackedResponse(conn, mediaType), err
} }
// DialHijack returns a hijacked connection with negotiated protocol proto. // DialHijack returns a hijacked connection with negotiated protocol proto.
@ -46,7 +47,8 @@ func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[s
} }
req = cli.addHeaders(req, meta) req = cli.addHeaders(req, meta)
return cli.setupHijackConn(ctx, req, proto) conn, _, err := cli.setupHijackConn(ctx, req, proto)
return conn, err
} }
// fallbackDial is used when WithDialer() was not called. // fallbackDial is used when WithDialer() was not called.
@ -61,7 +63,7 @@ func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
return net.Dial(proto, addr) return net.Dial(proto, addr)
} }
func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, error) { func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, string, error) {
req.Host = cli.addr req.Host = cli.addr
req.Header.Set("Connection", "Upgrade") req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", proto) req.Header.Set("Upgrade", proto)
@ -69,7 +71,7 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
dialer := cli.Dialer() dialer := cli.Dialer()
conn, err := dialer(ctx) conn, err := dialer(ctx)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") return nil, "", errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
} }
// When we set up a TCP connection for hijack, there could be long periods // When we set up a TCP connection for hijack, there could be long periods
@ -91,18 +93,18 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
//nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons //nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons
if err != httputil.ErrPersistEOF { if err != httputil.ErrPersistEOF {
if err != nil { if err != nil {
return nil, err return nil, "", err
} }
if resp.StatusCode != http.StatusSwitchingProtocols { if resp.StatusCode != http.StatusSwitchingProtocols {
resp.Body.Close() resp.Body.Close()
return nil, fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode) return nil, "", fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode)
} }
} }
c, br := clientconn.Hijack() c, br := clientconn.Hijack()
if br.Buffered() > 0 { if br.Buffered() > 0 {
// If there is buffered content, wrap the connection. We return an // If there is buffered content, wrap the connection. We return an
// object that implements CloseWrite iff the underlying connection // object that implements CloseWrite if the underlying connection
// implements it. // implements it.
if _, ok := c.(types.CloseWriter); ok { if _, ok := c.(types.CloseWriter); ok {
c = &hijackedConnCloseWriter{&hijackedConn{c, br}} c = &hijackedConnCloseWriter{&hijackedConn{c, br}}
@ -113,7 +115,13 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
br.Reset(nil) br.Reset(nil)
} }
return c, nil var mediaType string
if versions.GreaterThanOrEqualTo(cli.ClientVersion(), "1.42") {
// Prior to 1.42, Content-Type is always set to raw-stream and not relevant
mediaType = resp.Header.Get("Content-Type")
}
return c, mediaType, nil
} }
// hijackedConn wraps a net.Conn and is returned by setupHijackConn in the case // hijackedConn wraps a net.Conn and is returned by setupHijackConn in the case

@ -17,7 +17,7 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil) serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) return types.ImageInspect{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save