Skip to content

Commit

Permalink
Adds tests for normalizing reader and writer
Browse files Browse the repository at this point in the history
Signed-off-by: Emily Casey <ecasey@vmware.com>
  • Loading branch information
ekcasey committed Jul 20, 2020
1 parent 0766151 commit 53aa8c7
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 137 deletions.
61 changes: 1 addition & 60 deletions archive/tar.go → archive/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,71 +8,12 @@ import (
"path/filepath"
)

type PathInfo struct {
Path string
Info os.FileInfo
}

func WriteFilesToArchive(tw TarWriter, files []PathInfo) error {
for _, file := range files {
if err := AddFileToArchive(tw, file.Path, file.Info); err != nil {
return err
}
}
return nil
}

func AddFileToArchive(tw TarWriter, path string, fi os.FileInfo) error {
if fi.Mode()&os.ModeSocket != 0 {
return nil
}
var target string
if fi.Mode()&os.ModeSymlink != 0 {
var err error
target, err = os.Readlink(path)
if err != nil {
return err
}
}
header, err := tar.FileInfoHeader(fi, target)
if err != nil {
return err
}
header.Name = path

if err := tw.WriteHeader(header); err != nil {
return err
}
if fi.Mode().IsRegular() {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(tw, f); err != nil {
return err
}
}
return nil
}

func AddDirToArchive(tw TarWriter, srcDir string) error {
srcDir = filepath.Clean(srcDir)

return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
return AddFileToArchive(tw, file, fi)
})
}

type PathMode struct {
Path string
Mode os.FileMode
}

func Untar(tr TarReader) error {
func Extract(tr TarReader) error {
// Avoid umask from changing the file permissions in the tar file.
umask := setUmask(0)
defer setUmask(umask)
Expand Down
106 changes: 106 additions & 0 deletions archive/extract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package archive_test

import (
"archive/tar"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"testing"
"time"

"github.com/sclevine/spec"
"github.com/sclevine/spec/report"

"github.com/buildpacks/lifecycle/archive"
h "github.com/buildpacks/lifecycle/testhelpers"
)

func TestArchiveExtract(t *testing.T) {
rand.Seed(time.Now().UTC().UnixNano())
spec.Run(t, "extract", testExtract, spec.Report(report.Terminal{}))
}

func testExtract(t *testing.T, when spec.G, it spec.S) {
var tmpDir string

it.Before(func() {
var err error
tmpDir, err = ioutil.TempDir("", "archive-extract-test")
h.AssertNil(t, err)
})

it.After(func() {
h.AssertNil(t, os.RemoveAll(tmpDir))
})

when("#Extract", func() {
var pathModes = []archive.PathMode{
{"root", os.ModeDir + 0755},
{"root/readonly", os.ModeDir + 0500},
{"root/standarddir", os.ModeDir + 0755},
{"root/standarddir/somefile", 0644},
{"root/readonly/readonlysub/somefile", 0444},
{"root/readonly/readonlysub", os.ModeDir + 0500},
}

it.After(func() {
// Make all files os.ModePerm so they can all be cleaned up.
for _, pathMode := range pathModes {
extractedFile := filepath.Join(tmpDir, pathMode.Path)
if _, err := os.Stat(extractedFile); err == nil {
if err := os.Chmod(extractedFile, os.ModePerm); err != nil {
h.AssertNil(t, err)
}
}
}
})

it("extracts a tar file", func() {
file, err := os.Open(filepath.Join("testdata", "tar-to-dir", "some-archive.tar"))
h.AssertNil(t, err)
defer file.Close()

tr := archive.NewNormalizingTarReader(tar.NewReader(file))
tr.PrependDir(tmpDir)
h.AssertNil(t, archive.Extract(tr))

for _, pathMode := range pathModes {
extractedFile := filepath.Join(tmpDir, pathMode.Path)
fileInfo, err := os.Stat(extractedFile)
h.AssertNil(t, err)
h.AssertEq(t, fileInfo.Mode(), pathMode.Mode)
}
})

it("fails if file exists where directory needs to be created", func() {
_, err := os.Create(filepath.Join(tmpDir, "root"))
h.AssertNil(t, err)

file, err := os.Open(filepath.Join("testdata", "tar-to-dir", "some-archive.tar"))
h.AssertNil(t, err)
defer file.Close()
tr := archive.NewNormalizingTarReader(tar.NewReader(file))
tr.PrependDir(tmpDir)

h.AssertError(t, archive.Extract(tr), "root: not a directory")
})

it("doesn't alter permissions of existing folders", func() {
h.AssertNil(t, os.Mkdir(filepath.Join(tmpDir, "root"), 0744))
// Update permissions in case umask was applied.
h.AssertNil(t, os.Chmod(filepath.Join(tmpDir, "root"), 0744))

file, err := os.Open(filepath.Join("testdata", "tar-to-dir", "some-archive.tar"))
h.AssertNil(t, err)
defer file.Close()
tr := archive.NewNormalizingTarReader(tar.NewReader(file))
tr.PrependDir(tmpDir)

h.AssertNil(t, archive.Extract(tr))
fileInfo, err := os.Stat(filepath.Join(tmpDir, "root"))
h.AssertNil(t, err)
h.AssertEq(t, fileInfo.Mode(), os.ModeDir+0744)
})
})
}
2 changes: 1 addition & 1 deletion archive/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (tr *NormalizingTarReader) ExcludePaths(paths []string) {
tr.excludedPaths = paths
}

func (tr *NormalizingTarReader) ToWindows() {
func (tr *NormalizingTarReader) FromSlash() {
tr.headerOpts = append(tr.headerOpts, func(hdr *tar.Header) *tar.Header {
hdr.Name = filepath.FromSlash(hdr.Name)
return hdr
Expand Down
99 changes: 99 additions & 0 deletions archive/reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package archive_test

import (
"archive/tar"
"io"
"math/rand"
"runtime"
"testing"
"time"

"github.com/sclevine/spec"
"github.com/sclevine/spec/report"

"github.com/buildpacks/lifecycle/archive"
h "github.com/buildpacks/lifecycle/testhelpers"
)

func TestReader(t *testing.T) {
rand.Seed(time.Now().UTC().UnixNano())
spec.Run(t, "tar", testNormalizingTarReader, spec.Report(report.Terminal{}))
}

func testNormalizingTarReader(t *testing.T, when spec.G, it spec.S) {
when("NormalizingTarWriter", func() {
var (
ftr *fakeTarReader
ntr *archive.NormalizingTarReader
)

it.Before(func() {
ftr = &fakeTarReader{}
ntr = archive.NewNormalizingTarReader(ftr)
ftr.pushHeader(&tar.Header{Name: "some/path"})
})

when("#Strip", func() {
it("removes leading dirs", func() {
ntr.Strip("some")
hdr, err := ntr.Next()
h.AssertNil(t, err)
h.AssertEq(t, hdr.Name, "/path")
})
})

when("#FromSlash", func() {
it("converts path separators", func() {
ntr.FromSlash()
hdr, err := ntr.Next()
h.AssertNil(t, err)
if runtime.GOOS == "windows" {
h.AssertEq(t, hdr.Name, `some\path`)
} else {
h.AssertEq(t, hdr.Name, `some/path`)
}
})
})

when("#PrependDir", func() {
it("prepends the dir", func() {
ntr.PrependDir("/super-dir")
hdr, err := ntr.Next()
h.AssertNil(t, err)
h.AssertEq(t, hdr.Name, `/super-dir/some/path`)
})
})

when("#Exclude", func() {
it("skips excluded entries", func() {
ftr.pushHeader(&tar.Header{Name: "excluded-dir"})
ftr.pushHeader(&tar.Header{Name: "excluded-dir/file"})
ntr.ExcludePaths([]string{"excluded-dir"})
hdr, err := ntr.Next()
h.AssertNil(t, err)
h.AssertEq(t, hdr.Name, `some/path`)
})
})
})
}

type fakeTarReader struct {
hdrs []*tar.Header
}

func (r *fakeTarReader) Next() (*tar.Header, error) {
if len(r.hdrs) == 0 {
return nil, io.EOF
}
hdr := r.hdrs[0]
r.hdrs = r.hdrs[1:]
return hdr, nil
}

func (r *fakeTarReader) Read(b []byte) (int, error) {
return len(b), nil
}

func (r *fakeTarReader) pushHeader(hdr *tar.Header) {
r.hdrs = append([]*tar.Header{hdr}, r.hdrs...)
}
67 changes: 67 additions & 0 deletions archive/write.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package archive

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

type PathInfo struct {
Path string
Info os.FileInfo
}

func WriteFilesToArchive(tw TarWriter, files []PathInfo) error {
for _, file := range files {
if err := AddFileToArchive(tw, file.Path, file.Info); err != nil {
return err
}
}
return nil
}

func AddFileToArchive(tw TarWriter, path string, fi os.FileInfo) error {
if fi.Mode()&os.ModeSocket != 0 {
return nil
}
var target string
if fi.Mode()&os.ModeSymlink != 0 {
var err error
target, err = os.Readlink(path)
if err != nil {
return err
}
}
header, err := tar.FileInfoHeader(fi, target)
if err != nil {
return err
}
header.Name = path

if err := tw.WriteHeader(header); err != nil {
return err
}
if fi.Mode().IsRegular() {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(tw, f); err != nil {
return err
}
}
return nil
}

func AddDirToArchive(tw TarWriter, srcDir string) error {
srcDir = filepath.Clean(srcDir)

return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
return AddFileToArchive(tw, file, fi)
})
}
Loading

0 comments on commit 53aa8c7

Please sign in to comment.