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)
This commit is contained in:
parent
c32dde5baa
commit
db9cc91a9e
2 changed files with 110 additions and 2 deletions
|
@ -1309,6 +1309,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht
|
|||
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 {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
|
@ -1348,6 +1349,104 @@ func (s *Server) postContainersCopy(version version.Version, w http.ResponseWrit
|
|||
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 {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
|
@ -1536,6 +1635,9 @@ func createRouter(s *Server) *mux.Router {
|
|||
ProfilerSetup(r, "/debug/")
|
||||
}
|
||||
m := map[string]map[string]HttpApiFunc{
|
||||
"HEAD": {
|
||||
"/containers/{name:.*}/archive": s.headContainersArchive,
|
||||
},
|
||||
"GET": {
|
||||
"/_ping": s.ping,
|
||||
"/events": s.getEvents,
|
||||
|
@ -1557,6 +1659,7 @@ func createRouter(s *Server) *mux.Router {
|
|||
"/containers/{name:.*}/stats": s.getContainersStats,
|
||||
"/containers/{name:.*}/attach/ws": s.wsContainersAttach,
|
||||
"/exec/{id:.*}/json": s.getExecByID,
|
||||
"/containers/{name:.*}/archive": s.getContainersArchive,
|
||||
},
|
||||
"POST": {
|
||||
"/auth": s.postAuth,
|
||||
|
@ -1582,6 +1685,9 @@ func createRouter(s *Server) *mux.Router {
|
|||
"/exec/{name:.*}/resize": s.postContainerExecResize,
|
||||
"/containers/{name:.*}/rename": s.postContainerRename,
|
||||
},
|
||||
"PUT": {
|
||||
"/containers/{name:.*}/archive": s.putContainersArchive,
|
||||
},
|
||||
"DELETE": {
|
||||
"/containers/{name:.*}": s.deleteContainers,
|
||||
"/images/{name:.*}": s.deleteImages,
|
||||
|
|
|
@ -128,8 +128,10 @@ type CopyConfig struct {
|
|||
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 {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
|
|
Loading…
Reference in a new issue