From b3a0ff994450e60890ac8a81e80bbd49004ac8b6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 21 Jan 2024 15:02:49 +0100 Subject: [PATCH] api: remove POST /containers/{id}/copy endpoint (api < v1.23) 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 --- api/server/router/container/backend.go | 1 - api/server/router/container/container.go | 1 - api/server/router/container/copy.go | 41 +--------- daemon/archive.go | 19 ----- daemon/archive_unix.go | 49 ------------ integration-cli/docker_api_containers_test.go | 75 ------------------- integration/container/container_test.go | 1 - 7 files changed, 1 insertion(+), 186 deletions(-) diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index fa1c0dcc54..f35ab6a023 100644 --- a/api/server/router/container/backend.go +++ b/api/server/router/container/backend.go @@ -24,7 +24,6 @@ type execBackend interface { // copyBackend includes functions to implement to provide container copy functionality. type copyBackend interface { ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) - ContainerCopy(name string, res string) (io.ReadCloser, error) ContainerExport(ctx context.Context, name string, out io.Writer) error ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) diff --git a/api/server/router/container/container.go b/api/server/router/container/container.go index 7ac9dc7aac..ebe5f082c1 100644 --- a/api/server/router/container/container.go +++ b/api/server/router/container/container.go @@ -56,7 +56,6 @@ func (r *containerRouter) initRoutes() { router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait), router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize), router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach), - router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate), router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart), router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize), diff --git a/api/server/router/container/copy.go b/api/server/router/container/copy.go index 28920b6d7b..7596818e56 100644 --- a/api/server/router/container/copy.go +++ b/api/server/router/container/copy.go @@ -11,49 +11,10 @@ import ( "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/versions" gddohttputil "github.com/golang/gddo/httputil" ) -type pathError struct{} - -func (pathError) Error() string { - return "Path cannot be empty" -} - -func (pathError) InvalidParameter() {} - -// postContainersCopy is deprecated in favor of getContainersArchive. -// -// Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) -func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - version := httputils.VersionFromContext(ctx) - if versions.GreaterThanOrEqualTo(version, "1.24") { - w.WriteHeader(http.StatusNotFound) - return nil - } - - cfg := types.CopyConfig{} - if err := httputils.ReadJSON(r, &cfg); err != nil { - return err - } - - if cfg.Resource == "" { - return pathError{} - } - - data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource) - if err != nil { - return err - } - defer data.Close() - - w.Header().Set("Content-Type", "application/x-tar") - _, err = io.Copy(w, data) - return err -} - -// // Encode the stat to JSON, base64 encode, and place in a header. +// setContainerPathStatHeader encodes the stat to JSON, base64 encode, and place in a header. func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error { statJSON, err := json.Marshal(stat) if err != nil { diff --git a/daemon/archive.go b/daemon/archive.go index 4be02b25c6..65a67ffcc2 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -8,25 +8,6 @@ import ( "github.com/docker/docker/errdefs" ) -// ContainerCopy performs a deprecated operation of archiving the resource at -// the specified path in the container identified by the given name. -func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) { - ctr, err := daemon.GetContainer(name) - if err != nil { - return nil, err - } - - data, err := daemon.containerCopy(ctr, res) - if err == nil { - return data, nil - } - - if os.IsNotExist(err) { - return nil, containerFileNotFound{res, name} - } - return nil, errdefs.System(err) -} - // ContainerStatPath stats the filesystem resource at the specified path in the // container identified by the given name. func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) { diff --git a/daemon/archive_unix.go b/daemon/archive_unix.go index 837e671c2c..cbbf9c5f8a 100644 --- a/daemon/archive_unix.go +++ b/daemon/archive_unix.go @@ -161,55 +161,6 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path return nil } -func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) { - container.Lock() - - defer func() { - if err != nil { - // Wait to unlock the container until the archive is fully read - // (see the ReadCloseWrapper func below) or if there is an error - // before that occurs. - container.Unlock() - } - }() - - cfs, err := daemon.openContainerFS(container) - if err != nil { - return nil, err - } - defer func() { - if err != nil { - cfs.Close() - } - }() - - err = cfs.RunInFS(context.TODO(), func() error { - _, err := os.Stat(resource) - return err - }) - if err != nil { - return nil, err - } - - tb, err := archive.NewTarballer(resource, &archive.TarOptions{ - Compression: archive.Uncompressed, - }) - if err != nil { - return nil, err - } - - cfs.GoInFS(context.TODO(), tb.Do) - archv := tb.Reader() - reader := ioutils.NewReadCloserWrapper(archv, func() error { - err := archv.Close() - _ = cfs.Close() - container.Unlock() - return err - }) - daemon.LogContainerEvent(container, events.ActionCopy) - return reader, nil -} - // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it // cannot be in a read-only volume. If it is not in a volume, the container // cannot be configured with a read-only rootfs. diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index fdab67e0fc..52fb611d1f 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -958,81 +958,6 @@ func (s *DockerAPISuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) { assert.Equal(c, res.StatusCode, http.StatusNotFound) } -func (s *DockerAPISuite) TestContainerAPICopyPre124(c *testing.T) { - testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later - const name = "test-container-api-copy" - cli.DockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") - - postData := types.CopyConfig{ - Resource: "/test.txt", - } - - res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) - assert.NilError(c, err) - assert.Equal(c, res.StatusCode, http.StatusOK) - - found := false - for tarReader := tar.NewReader(body); ; { - h, err := tarReader.Next() - if err != nil { - if err == io.EOF { - break - } - c.Fatal(err) - } - if h.Name == "test.txt" { - found = true - break - } - } - assert.Assert(c, found) -} - -func (s *DockerAPISuite) TestContainerAPICopyResourcePathEmptyPre124(c *testing.T) { - testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later - const name = "test-container-api-copy-resource-empty" - cli.DockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") - - postData := types.CopyConfig{ - Resource: "", - } - - res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) - assert.NilError(c, err) - assert.Equal(c, res.StatusCode, http.StatusBadRequest) - b, err := request.ReadBody(body) - assert.NilError(c, err) - assert.Assert(c, is.Regexp("^Path cannot be empty\n$", string(b))) -} - -func (s *DockerAPISuite) TestContainerAPICopyResourcePathNotFoundPre124(c *testing.T) { - testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later - const name = "test-container-api-copy-resource-not-found" - cli.DockerCmd(c, "run", "--name", name, "busybox") - - postData := types.CopyConfig{ - Resource: "/notexist", - } - - res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) - assert.NilError(c, err) - assert.Equal(c, res.StatusCode, http.StatusNotFound) - b, err := request.ReadBody(body) - assert.NilError(c, err) - assert.Assert(c, is.Regexp("^Could not find the file /notexist in container "+name+"\n$", string(b))) -} - -func (s *DockerAPISuite) TestContainerAPICopyContainerNotFoundPr124(c *testing.T) { - testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later - postData := types.CopyConfig{ - Resource: "/something", - } - - res, _, err := request.Post(testutil.GetContext(c), "/v1.23/containers/notexists/copy", request.JSONBody(postData)) - assert.NilError(c, err) - assert.Equal(c, res.StatusCode, http.StatusNotFound) -} - func (s *DockerAPISuite) TestContainerAPIDelete(c *testing.T) { id := runSleepingContainer(c) cli.WaitRun(c, id) diff --git a/integration/container/container_test.go b/integration/container/container_test.go index aa62cb89e7..65394ec75b 100644 --- a/integration/container/container_test.go +++ b/integration/container/container_test.go @@ -29,7 +29,6 @@ func TestContainerInvalidJSON(t *testing.T) { if runtime.GOOS != "windows" { endpoints = append( endpoints, - "/v1.23/containers/foobar/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24) "/v1.23/containers/foobar/start", // accepts a body on API < v1.24 ) }