This endpoint was deprecated in API v1.20 (Docker Engine v1.8.0) in
commit db9cc91a9ef7dea4c8d85f64578889cec3dd99b2, in favor of the
`PUT /containers/{id}/archive` and `HEAD /containers/{id}/archive`
endpoints, and disabled in API v1.24 (Docker Engine v1.12.0) through
commit 428328908dc529b1678fb3d8b033fb0591a294e3.
This patch removes the endpoint, and the associated `daemon.ContainerCopy`
method in the backend.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -24,7 +24,6 @@ type execBackend interface {
|
| 24 | 24 |
// copyBackend includes functions to implement to provide container copy functionality. |
| 25 | 25 |
type copyBackend interface {
|
| 26 | 26 |
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) |
| 27 |
- ContainerCopy(name string, res string) (io.ReadCloser, error) |
|
| 28 | 27 |
ContainerExport(ctx context.Context, name string, out io.Writer) error |
| 29 | 28 |
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error |
| 30 | 29 |
ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) |
| ... | ... |
@@ -56,7 +56,6 @@ func (r *containerRouter) initRoutes() {
|
| 56 | 56 |
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
| 57 | 57 |
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
| 58 | 58 |
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
| 59 |
- router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
|
| 60 | 59 |
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
| 61 | 60 |
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
| 62 | 61 |
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
| ... | ... |
@@ -11,49 +11,10 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
"github.com/docker/docker/api/server/httputils" |
| 13 | 13 |
"github.com/docker/docker/api/types" |
| 14 |
- "github.com/docker/docker/api/types/versions" |
|
| 15 | 14 |
gddohttputil "github.com/golang/gddo/httputil" |
| 16 | 15 |
) |
| 17 | 16 |
|
| 18 |
-type pathError struct{}
|
|
| 19 |
- |
|
| 20 |
-func (pathError) Error() string {
|
|
| 21 |
- return "Path cannot be empty" |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-func (pathError) InvalidParameter() {}
|
|
| 25 |
- |
|
| 26 |
-// postContainersCopy is deprecated in favor of getContainersArchive. |
|
| 27 |
-// |
|
| 28 |
-// Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) |
|
| 29 |
-func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 30 |
- version := httputils.VersionFromContext(ctx) |
|
| 31 |
- if versions.GreaterThanOrEqualTo(version, "1.24") {
|
|
| 32 |
- w.WriteHeader(http.StatusNotFound) |
|
| 33 |
- return nil |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- cfg := types.CopyConfig{}
|
|
| 37 |
- if err := httputils.ReadJSON(r, &cfg); err != nil {
|
|
| 38 |
- return err |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- if cfg.Resource == "" {
|
|
| 42 |
- return pathError{}
|
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource) |
|
| 46 |
- if err != nil {
|
|
| 47 |
- return err |
|
| 48 |
- } |
|
| 49 |
- defer data.Close() |
|
| 50 |
- |
|
| 51 |
- w.Header().Set("Content-Type", "application/x-tar")
|
|
| 52 |
- _, err = io.Copy(w, data) |
|
| 53 |
- return err |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-// // Encode the stat to JSON, base64 encode, and place in a header. |
|
| 17 |
+// setContainerPathStatHeader encodes the stat to JSON, base64 encode, and place in a header. |
|
| 57 | 18 |
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
| 58 | 19 |
statJSON, err := json.Marshal(stat) |
| 59 | 20 |
if err != nil {
|
| ... | ... |
@@ -8,25 +8,6 @@ import ( |
| 8 | 8 |
"github.com/docker/docker/errdefs" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
-// ContainerCopy performs a deprecated operation of archiving the resource at |
|
| 12 |
-// the specified path in the container identified by the given name. |
|
| 13 |
-func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) {
|
|
| 14 |
- ctr, err := daemon.GetContainer(name) |
|
| 15 |
- if err != nil {
|
|
| 16 |
- return nil, err |
|
| 17 |
- } |
|
| 18 |
- |
|
| 19 |
- data, err := daemon.containerCopy(ctr, res) |
|
| 20 |
- if err == nil {
|
|
| 21 |
- return data, nil |
|
| 22 |
- } |
|
| 23 |
- |
|
| 24 |
- if os.IsNotExist(err) {
|
|
| 25 |
- return nil, containerFileNotFound{res, name}
|
|
| 26 |
- } |
|
| 27 |
- return nil, errdefs.System(err) |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 | 11 |
// ContainerStatPath stats the filesystem resource at the specified path in the |
| 31 | 12 |
// container identified by the given name. |
| 32 | 13 |
func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) {
|
| ... | ... |
@@ -161,55 +161,6 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path |
| 161 | 161 |
return nil |
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 |
-func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) {
|
|
| 165 |
- container.Lock() |
|
| 166 |
- |
|
| 167 |
- defer func() {
|
|
| 168 |
- if err != nil {
|
|
| 169 |
- // Wait to unlock the container until the archive is fully read |
|
| 170 |
- // (see the ReadCloseWrapper func below) or if there is an error |
|
| 171 |
- // before that occurs. |
|
| 172 |
- container.Unlock() |
|
| 173 |
- } |
|
| 174 |
- }() |
|
| 175 |
- |
|
| 176 |
- cfs, err := daemon.openContainerFS(container) |
|
| 177 |
- if err != nil {
|
|
| 178 |
- return nil, err |
|
| 179 |
- } |
|
| 180 |
- defer func() {
|
|
| 181 |
- if err != nil {
|
|
| 182 |
- cfs.Close() |
|
| 183 |
- } |
|
| 184 |
- }() |
|
| 185 |
- |
|
| 186 |
- err = cfs.RunInFS(context.TODO(), func() error {
|
|
| 187 |
- _, err := os.Stat(resource) |
|
| 188 |
- return err |
|
| 189 |
- }) |
|
| 190 |
- if err != nil {
|
|
| 191 |
- return nil, err |
|
| 192 |
- } |
|
| 193 |
- |
|
| 194 |
- tb, err := archive.NewTarballer(resource, &archive.TarOptions{
|
|
| 195 |
- Compression: archive.Uncompressed, |
|
| 196 |
- }) |
|
| 197 |
- if err != nil {
|
|
| 198 |
- return nil, err |
|
| 199 |
- } |
|
| 200 |
- |
|
| 201 |
- cfs.GoInFS(context.TODO(), tb.Do) |
|
| 202 |
- archv := tb.Reader() |
|
| 203 |
- reader := ioutils.NewReadCloserWrapper(archv, func() error {
|
|
| 204 |
- err := archv.Close() |
|
| 205 |
- _ = cfs.Close() |
|
| 206 |
- container.Unlock() |
|
| 207 |
- return err |
|
| 208 |
- }) |
|
| 209 |
- daemon.LogContainerEvent(container, events.ActionCopy) |
|
| 210 |
- return reader, nil |
|
| 211 |
-} |
|
| 212 |
- |
|
| 213 | 164 |
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it |
| 214 | 165 |
// cannot be in a read-only volume. If it is not in a volume, the container |
| 215 | 166 |
// cannot be configured with a read-only rootfs. |
| ... | ... |
@@ -958,81 +958,6 @@ func (s *DockerAPISuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) {
|
| 958 | 958 |
assert.Equal(c, res.StatusCode, http.StatusNotFound) |
| 959 | 959 |
} |
| 960 | 960 |
|
| 961 |
-func (s *DockerAPISuite) TestContainerAPICopyPre124(c *testing.T) {
|
|
| 962 |
- testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
|
| 963 |
- const name = "test-container-api-copy" |
|
| 964 |
- cli.DockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") |
|
| 965 |
- |
|
| 966 |
- postData := types.CopyConfig{
|
|
| 967 |
- Resource: "/test.txt", |
|
| 968 |
- } |
|
| 969 |
- |
|
| 970 |
- res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) |
|
| 971 |
- assert.NilError(c, err) |
|
| 972 |
- assert.Equal(c, res.StatusCode, http.StatusOK) |
|
| 973 |
- |
|
| 974 |
- found := false |
|
| 975 |
- for tarReader := tar.NewReader(body); ; {
|
|
| 976 |
- h, err := tarReader.Next() |
|
| 977 |
- if err != nil {
|
|
| 978 |
- if err == io.EOF {
|
|
| 979 |
- break |
|
| 980 |
- } |
|
| 981 |
- c.Fatal(err) |
|
| 982 |
- } |
|
| 983 |
- if h.Name == "test.txt" {
|
|
| 984 |
- found = true |
|
| 985 |
- break |
|
| 986 |
- } |
|
| 987 |
- } |
|
| 988 |
- assert.Assert(c, found) |
|
| 989 |
-} |
|
| 990 |
- |
|
| 991 |
-func (s *DockerAPISuite) TestContainerAPICopyResourcePathEmptyPre124(c *testing.T) {
|
|
| 992 |
- testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
|
| 993 |
- const name = "test-container-api-copy-resource-empty" |
|
| 994 |
- cli.DockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") |
|
| 995 |
- |
|
| 996 |
- postData := types.CopyConfig{
|
|
| 997 |
- Resource: "", |
|
| 998 |
- } |
|
| 999 |
- |
|
| 1000 |
- res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) |
|
| 1001 |
- assert.NilError(c, err) |
|
| 1002 |
- assert.Equal(c, res.StatusCode, http.StatusBadRequest) |
|
| 1003 |
- b, err := request.ReadBody(body) |
|
| 1004 |
- assert.NilError(c, err) |
|
| 1005 |
- assert.Assert(c, is.Regexp("^Path cannot be empty\n$", string(b)))
|
|
| 1006 |
-} |
|
| 1007 |
- |
|
| 1008 |
-func (s *DockerAPISuite) TestContainerAPICopyResourcePathNotFoundPre124(c *testing.T) {
|
|
| 1009 |
- testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
|
| 1010 |
- const name = "test-container-api-copy-resource-not-found" |
|
| 1011 |
- cli.DockerCmd(c, "run", "--name", name, "busybox") |
|
| 1012 |
- |
|
| 1013 |
- postData := types.CopyConfig{
|
|
| 1014 |
- Resource: "/notexist", |
|
| 1015 |
- } |
|
| 1016 |
- |
|
| 1017 |
- res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) |
|
| 1018 |
- assert.NilError(c, err) |
|
| 1019 |
- assert.Equal(c, res.StatusCode, http.StatusNotFound) |
|
| 1020 |
- b, err := request.ReadBody(body) |
|
| 1021 |
- assert.NilError(c, err) |
|
| 1022 |
- assert.Assert(c, is.Regexp("^Could not find the file /notexist in container "+name+"\n$", string(b)))
|
|
| 1023 |
-} |
|
| 1024 |
- |
|
| 1025 |
-func (s *DockerAPISuite) TestContainerAPICopyContainerNotFoundPr124(c *testing.T) {
|
|
| 1026 |
- testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later |
|
| 1027 |
- postData := types.CopyConfig{
|
|
| 1028 |
- Resource: "/something", |
|
| 1029 |
- } |
|
| 1030 |
- |
|
| 1031 |
- res, _, err := request.Post(testutil.GetContext(c), "/v1.23/containers/notexists/copy", request.JSONBody(postData)) |
|
| 1032 |
- assert.NilError(c, err) |
|
| 1033 |
- assert.Equal(c, res.StatusCode, http.StatusNotFound) |
|
| 1034 |
-} |
|
| 1035 |
- |
|
| 1036 | 961 |
func (s *DockerAPISuite) TestContainerAPIDelete(c *testing.T) {
|
| 1037 | 962 |
id := runSleepingContainer(c) |
| 1038 | 963 |
cli.WaitRun(c, id) |
| ... | ... |
@@ -29,7 +29,6 @@ func TestContainerInvalidJSON(t *testing.T) {
|
| 29 | 29 |
if runtime.GOOS != "windows" {
|
| 30 | 30 |
endpoints = append( |
| 31 | 31 |
endpoints, |
| 32 |
- "/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) |
|
| 33 | 32 |
"/v1.23/containers/foobar/start", // accepts a body on API < v1.24 |
| 34 | 33 |
) |
| 35 | 34 |
} |