| package docker |
| |
| import ( |
| "errors" |
| "io" |
| "io/ioutil" |
| "os/exec" |
| ) |
| |
| type Archive io.Reader |
| |
| type Compression uint32 |
| |
| const ( |
| Uncompressed Compression = iota |
| Bzip2 |
| Gzip |
| Xz |
| ) |
| |
| func (compression *Compression) Flag() string { |
| switch *compression { |
| case Bzip2: |
| return "j" |
| case Gzip: |
| return "z" |
| case Xz: |
| return "J" |
| } |
| return "" |
| } |
| |
| func Tar(path string, compression Compression) (io.Reader, error) { |
| cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".") |
| return CmdStream(cmd) |
| } |
| |
| func Untar(archive io.Reader, path string) error { |
| cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x") |
| cmd.Stdin = archive |
| output, err := cmd.CombinedOutput() |
| if err != nil { |
| return errors.New(err.Error() + ": " + string(output)) |
| } |
| return nil |
| } |
| |
| // CmdStream executes a command, and returns its stdout as a stream. |
| // If the command fails to run or doesn't complete successfully, an error |
| // will be returned, including anything written on stderr. |
| func CmdStream(cmd *exec.Cmd) (io.Reader, error) { |
| stdout, err := cmd.StdoutPipe() |
| if err != nil { |
| return nil, err |
| } |
| stderr, err := cmd.StderrPipe() |
| if err != nil { |
| return nil, err |
| } |
| pipeR, pipeW := io.Pipe() |
| errChan := make(chan []byte) |
| // Collect stderr, we will use it in case of an error |
| go func() { |
| errText, e := ioutil.ReadAll(stderr) |
| if e != nil { |
| errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")") |
| } |
| errChan <- errText |
| }() |
| // Copy stdout to the returned pipe |
| go func() { |
| _, err := io.Copy(pipeW, stdout) |
| if err != nil { |
| pipeW.CloseWithError(err) |
| } |
| errText := <-errChan |
| if err := cmd.Wait(); err != nil { |
| pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText))) |
| } else { |
| pipeW.Close() |
| } |
| }() |
| // Run the command and return the pipe |
| if err := cmd.Start(); err != nil { |
| return nil, err |
| } |
| return pipeR, nil |
| } |