archive.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. Xz
  15. )
  16. func (compression *Compression) Flag() string {
  17. switch *compression {
  18. case Bzip2:
  19. return "j"
  20. case Gzip:
  21. return "z"
  22. case Xz:
  23. return "J"
  24. }
  25. return ""
  26. }
  27. func Tar(path string, compression Compression) (io.Reader, error) {
  28. cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")
  29. return CmdStream(cmd)
  30. }
  31. func Untar(archive io.Reader, path string) error {
  32. cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
  33. cmd.Stdin = archive
  34. output, err := cmd.CombinedOutput()
  35. if err != nil {
  36. return errors.New(err.Error() + ": " + string(output))
  37. }
  38. return nil
  39. }
  40. // CmdStream executes a command, and returns its stdout as a stream.
  41. // If the command fails to run or doesn't complete successfully, an error
  42. // will be returned, including anything written on stderr.
  43. func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
  44. stdout, err := cmd.StdoutPipe()
  45. if err != nil {
  46. return nil, err
  47. }
  48. stderr, err := cmd.StderrPipe()
  49. if err != nil {
  50. return nil, err
  51. }
  52. pipeR, pipeW := io.Pipe()
  53. errChan := make(chan []byte)
  54. // Collect stderr, we will use it in case of an error
  55. go func() {
  56. errText, e := ioutil.ReadAll(stderr)
  57. if e != nil {
  58. errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
  59. }
  60. errChan <- errText
  61. }()
  62. // Copy stdout to the returned pipe
  63. go func() {
  64. _, err := io.Copy(pipeW, stdout)
  65. if err != nil {
  66. pipeW.CloseWithError(err)
  67. }
  68. errText := <-errChan
  69. if err := cmd.Wait(); err != nil {
  70. pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
  71. } else {
  72. pipeW.Close()
  73. }
  74. }()
  75. // Run the command and return the pipe
  76. if err := cmd.Start(); err != nil {
  77. return nil, err
  78. }
  79. return pipeR, nil
  80. }