Bladeren bron

api/server: StatPath, ArchivePath, ExtractToDir

Adds http handlers for new API endpoints:

GET ContainersArchivePath
  Return a Tar Archive of the contents at the specified location in a
  container. Deprecates POST ContainersCopy. Use a HEAD request to stat
  the resource.

PUT ContainersExtractToDir
  Extract the Tar Archive from the request body to the directory at the
  specified location inside a container.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
Josh Hawn 10 jaren geleden
bovenliggende
commit
db9cc91a9e
2 gewijzigde bestanden met toevoegingen van 110 en 2 verwijderingen
  1. 106 0
      api/server/server.go
  2. 4 2
      api/types/types.go

+ 106 - 0
api/server/server.go

@@ -1309,6 +1309,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht
 	return nil
 	return nil
 }
 }
 
 
+// postContainersCopy is deprecated in favor of getContainersArchivePath.
 func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
@@ -1348,6 +1349,104 @@ func (s *Server) postContainersCopy(version version.Version, w http.ResponseWrit
 	return nil
 	return nil
 }
 }
 
 
+// // Encode 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 {
+		return err
+	}
+
+	header.Set(
+		"X-Docker-Container-Path-Stat",
+		base64.StdEncoding.EncodeToString(statJSON),
+	)
+
+	return nil
+}
+
+func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if vars == nil {
+		return fmt.Errorf("Missing parameter")
+	}
+	if err := parseForm(r); err != nil {
+		return err
+	}
+
+	name := vars["name"]
+	path := r.Form.Get("path")
+
+	switch {
+	case name == "":
+		return fmt.Errorf("bad parameter: 'name' cannot be empty")
+	case path == "":
+		return fmt.Errorf("bad parameter: 'path' cannot be empty")
+	}
+
+	stat, err := s.daemon.ContainerStatPath(name, path)
+	if err != nil {
+		return err
+	}
+
+	return setContainerPathStatHeader(stat, w.Header())
+}
+
+func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if vars == nil {
+		return fmt.Errorf("Missing parameter")
+	}
+	if err := parseForm(r); err != nil {
+		return err
+	}
+
+	name := vars["name"]
+	path := r.Form.Get("path")
+
+	switch {
+	case name == "":
+		return fmt.Errorf("bad parameter: 'name' cannot be empty")
+	case path == "":
+		return fmt.Errorf("bad parameter: 'path' cannot be empty")
+	}
+
+	tarArchive, stat, err := s.daemon.ContainerArchivePath(name, path)
+	if err != nil {
+		return err
+	}
+	defer tarArchive.Close()
+
+	if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
+		return err
+	}
+
+	w.Header().Set("Content-Type", "application/x-tar")
+	_, err = io.Copy(w, tarArchive)
+
+	return err
+}
+
+func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if vars == nil {
+		return fmt.Errorf("Missing parameter")
+	}
+	if err := parseForm(r); err != nil {
+		return err
+	}
+
+	name := vars["name"]
+	path := r.Form.Get("path")
+
+	noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir")
+
+	switch {
+	case name == "":
+		return fmt.Errorf("bad parameter: 'name' cannot be empty")
+	case path == "":
+		return fmt.Errorf("bad parameter: 'path' cannot be empty")
+	}
+
+	return s.daemon.ContainerExtractToDir(name, path, noOverwriteDirNonDir, r.Body)
+}
+
 func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
@@ -1536,6 +1635,9 @@ func createRouter(s *Server) *mux.Router {
 		ProfilerSetup(r, "/debug/")
 		ProfilerSetup(r, "/debug/")
 	}
 	}
 	m := map[string]map[string]HttpApiFunc{
 	m := map[string]map[string]HttpApiFunc{
+		"HEAD": {
+			"/containers/{name:.*}/archive": s.headContainersArchive,
+		},
 		"GET": {
 		"GET": {
 			"/_ping":                          s.ping,
 			"/_ping":                          s.ping,
 			"/events":                         s.getEvents,
 			"/events":                         s.getEvents,
@@ -1557,6 +1659,7 @@ func createRouter(s *Server) *mux.Router {
 			"/containers/{name:.*}/stats":     s.getContainersStats,
 			"/containers/{name:.*}/stats":     s.getContainersStats,
 			"/containers/{name:.*}/attach/ws": s.wsContainersAttach,
 			"/containers/{name:.*}/attach/ws": s.wsContainersAttach,
 			"/exec/{id:.*}/json":              s.getExecByID,
 			"/exec/{id:.*}/json":              s.getExecByID,
+			"/containers/{name:.*}/archive":   s.getContainersArchive,
 		},
 		},
 		"POST": {
 		"POST": {
 			"/auth":                         s.postAuth,
 			"/auth":                         s.postAuth,
@@ -1582,6 +1685,9 @@ func createRouter(s *Server) *mux.Router {
 			"/exec/{name:.*}/resize":        s.postContainerExecResize,
 			"/exec/{name:.*}/resize":        s.postContainerExecResize,
 			"/containers/{name:.*}/rename":  s.postContainerRename,
 			"/containers/{name:.*}/rename":  s.postContainerRename,
 		},
 		},
+		"PUT": {
+			"/containers/{name:.*}/archive": s.putContainersArchive,
+		},
 		"DELETE": {
 		"DELETE": {
 			"/containers/{name:.*}": s.deleteContainers,
 			"/containers/{name:.*}": s.deleteContainers,
 			"/images/{name:.*}":     s.deleteImages,
 			"/images/{name:.*}":     s.deleteImages,

+ 4 - 2
api/types/types.go

@@ -128,8 +128,10 @@ type CopyConfig struct {
 	Resource string
 	Resource string
 }
 }
 
 
-// ContainerPathStat is used to encode the response from
-// 	GET /containers/{name:.*}/stat-path
+// ContainerPathStat is used to encode the header from
+// 	GET /containers/{name:.*}/archive
+// "name" is the file or directory name.
+// "path" is the absolute path to the resource in the container.
 type ContainerPathStat struct {
 type ContainerPathStat struct {
 	Name  string      `json:"name"`
 	Name  string      `json:"name"`
 	Path  string      `json:"path"`
 	Path  string      `json:"path"`