Skip to content

Commit

Permalink
decompressor/tar: add initial implementation based on tgz
Browse files Browse the repository at this point in the history
This passes the tests, but I am not a go developer and success may not
mean much. See hashicorp#19.
  • Loading branch information
ketzacoatl committed Apr 11, 2016
1 parent ef5edd3 commit 168d551
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 0 deletions.
1 change: 1 addition & 0 deletions decompress.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func init() {
Decompressors = map[string]Decompressor{
"bz2": new(Bzip2Decompressor),
"gz": new(GzipDecompressor),
"tar": new(TarDecompressor),
"tar.bz2": tbzDecompressor,
"tar.gz": tgzDecompressor,
"tbz2": tbzDecompressor,
Expand Down
90 changes: 90 additions & 0 deletions decompress_tar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package getter

import (
"archive/tar"
"fmt"
"io"
"os"
"path/filepath"
)

// TarDecompressor is an implementation of Decompressor that unarchives tar files.
type TarDecompressor struct{}

func (d *TarDecompressor) Decompress(dst, src string, dir bool) error {
// If we're going into a directory we should make that first
mkdir := dst
if !dir {
mkdir = filepath.Dir(dst)
}
if err := os.MkdirAll(mkdir, 0755); err != nil {
return err
}

// File first
f, err := os.Open(src)
if err != nil {
return err
}
defer f.Close()

// nothing to decompress, tar is next
tarR := tar.NewReader(f)
done := false
for {
hdr, err := tarR.Next()
if err == io.EOF {
if !done {
// Empty archive
return fmt.Errorf("empty archive: %s", src)
}

return nil
}
if err != nil {
return err
}

path := dst
if dir {
path = filepath.Join(path, hdr.Name)
}

if hdr.FileInfo().IsDir() {
if !dir {
return fmt.Errorf("expected a single file: %s", src)
}

// A directory, just make the directory and continue unarchiving...
if err := os.MkdirAll(path, 0755); err != nil {
return err
}

continue
}

// We have a file. If we already decoded, then it is an error
if !dir && done {
return fmt.Errorf("expected a single file, got multiple: %s", src)
}

// Mark that we're done so future in single file mode errors
done = true

// Open the file for writing
dstF, err := os.Create(path)
if err != nil {
return err
}
_, err = io.Copy(dstF, tarR)
dstF.Close()
if err != nil {
return err
}

// Chmod the file
if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil {
return err
}
}
}
71 changes: 71 additions & 0 deletions decompress_tar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package getter

import (
"path/filepath"
"runtime"
"testing"
)

func TestTarDecompressor(t *testing.T) {

multiplePaths := []string{"dir/", "dir/test2", "test1"}
if runtime.GOOS == "windows" {
multiplePaths = []string{"dir/", "dir\\test2", "test1"}
}

cases := []TestDecompressCase{
{
"empty.tar",
false,
true,
nil,
"",
},

{
"single.tar",
false,
false,
nil,
"d3b07384d113edec49eaa6238ad5ff00",
},

{
"single.tar",
true,
false,
[]string{"file"},
"",
},

{
"multiple.tar",
true,
false,
[]string{"file1", "file2"},
"",
},

{
"multiple.tar",
false,
true,
nil,
"",
},

{
"multiple_dir.tar",
true,
false,
multiplePaths,
"",
},
}

for i, tc := range cases {
cases[i].Input = filepath.Join("./test-fixtures", "decompress-tar", tc.Input)
}

TestDecompressor(t, new(TarDecompressor), cases)
}
Binary file added test-fixtures/decompress-tar/empty.tar
Binary file not shown.
Binary file added test-fixtures/decompress-tar/multiple.tar
Binary file not shown.
Binary file added test-fixtures/decompress-tar/multiple_dir.tar
Binary file not shown.
Binary file added test-fixtures/decompress-tar/single.tar
Binary file not shown.

0 comments on commit 168d551

Please sign in to comment.