api: remove POST /containers/{id}/copy endpoint (api < v1.23)
This endpoint was deprecated in API v1.20 (Docker Engine v1.8.0) in commitdb9cc91a9e
, 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 commit428328908d
. This patch removes the endpoint, and the associated `daemon.ContainerCopy` method in the backend. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
83f790cccc
commit
b3a0ff9944
7 changed files with 1 additions and 186 deletions
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue