copy.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package lib
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "path/filepath"
  10. "strings"
  11. "github.com/docker/docker/api/types"
  12. )
  13. // CopyToContainerOptions holds information
  14. // about files to copy into a container
  15. type CopyToContainerOptions struct {
  16. ContainerID string
  17. Path string
  18. Content io.Reader
  19. AllowOverwriteDirWithFile bool
  20. }
  21. // StatContainerPath returns Stat information about a path inside the container filesystem.
  22. func (cli *Client) StatContainerPath(containerID, path string) (types.ContainerPathStat, error) {
  23. query := make(url.Values, 1)
  24. query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
  25. urlStr := fmt.Sprintf("/containers/%s/archive", containerID)
  26. response, err := cli.HEAD(urlStr, query, nil)
  27. if err != nil {
  28. return types.ContainerPathStat{}, err
  29. }
  30. defer ensureReaderClosed(response)
  31. return getContainerPathStatFromHeader(response.header)
  32. }
  33. // CopyToContainer copies content into the container filesystem.
  34. func (cli *Client) CopyToContainer(options CopyToContainerOptions) error {
  35. var query url.Values
  36. query.Set("path", filepath.ToSlash(options.Path)) // Normalize the paths used in the API.
  37. // Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
  38. if !options.AllowOverwriteDirWithFile {
  39. query.Set("noOverwriteDirNonDir", "true")
  40. }
  41. path := fmt.Sprintf("/containers/%s/archive", options.ContainerID)
  42. response, err := cli.PUT(path, query, options.Content, nil)
  43. if err != nil {
  44. return err
  45. }
  46. defer ensureReaderClosed(response)
  47. if response.statusCode != http.StatusOK {
  48. return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
  49. }
  50. return nil
  51. }
  52. // CopyFromContainer get the content from the container and return it as a Reader
  53. // to manipulate it in the host. It's up to the caller to close the reader.
  54. func (cli *Client) CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
  55. query := make(url.Values, 1)
  56. query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
  57. apiPath := fmt.Sprintf("/containers/%s/archive", containerID)
  58. response, err := cli.GET(apiPath, query, nil)
  59. if err != nil {
  60. return nil, types.ContainerPathStat{}, err
  61. }
  62. if response.statusCode != http.StatusOK {
  63. return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
  64. }
  65. // In order to get the copy behavior right, we need to know information
  66. // about both the source and the destination. The response headers include
  67. // stat info about the source that we can use in deciding exactly how to
  68. // copy it locally. Along with the stat info about the local destination,
  69. // we have everything we need to handle the multiple possibilities there
  70. // can be when copying a file/dir from one location to another file/dir.
  71. stat, err := getContainerPathStatFromHeader(response.header)
  72. if err != nil {
  73. return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
  74. }
  75. return response.body, stat, err
  76. }
  77. func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
  78. var stat types.ContainerPathStat
  79. encodedStat := header.Get("X-Docker-Container-Path-Stat")
  80. statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
  81. err := json.NewDecoder(statDecoder).Decode(&stat)
  82. if err != nil {
  83. err = fmt.Errorf("unable to decode container path stat header: %s", err)
  84. }
  85. return stat, err
  86. }