archive.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package docker
  2. import (
  3. "errors"
  4. "io"
  5. "io/ioutil"
  6. "os/exec"
  7. )
  8. type Archive io.Reader
  9. type Compression uint32
  10. const (
  11. Uncompressed Compression = iota
  12. Bzip2
  13. Gzip
  14. )
  15. func (compression *Compression) Flag() string {
  16. switch *compression {
  17. case Bzip2:
  18. return "j"
  19. case Gzip:
  20. return "z"
  21. }
  22. return ""
  23. }
  24. func Tar(path string, compression Compression) (io.Reader, error) {
  25. cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")
  26. return CmdStream(cmd)
  27. }
  28. func Untar(archive io.Reader, path string) error {
  29. cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
  30. cmd.Stdin = archive
  31. output, err := cmd.CombinedOutput()
  32. if err != nil {
  33. return errors.New(err.Error() + ": " + string(output))
  34. }
  35. return nil
  36. }
  37. func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
  38. stdout, err := cmd.StdoutPipe()
  39. if err != nil {
  40. return nil, err
  41. }
  42. stderr, err := cmd.StderrPipe()
  43. if err != nil {
  44. return nil, err
  45. }
  46. pipeR, pipeW := io.Pipe()
  47. go func() {
  48. _, err := io.Copy(pipeW, stdout)
  49. if err != nil {
  50. pipeW.CloseWithError(err)
  51. }
  52. errText, e := ioutil.ReadAll(stderr)
  53. if e != nil {
  54. errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
  55. }
  56. if err := cmd.Wait(); err != nil {
  57. // FIXME: can this block if stderr outputs more than the size of StderrPipe()'s buffer?
  58. pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
  59. } else {
  60. pipeW.Close()
  61. }
  62. }()
  63. if err := cmd.Start(); err != nil {
  64. return nil, err
  65. }
  66. return pipeR, nil
  67. }