diff --git a/go.mod b/go.mod index 6cbf85af..8424f58b 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,7 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 - github.com/MasterMinds/semver v1.5.0 github.com/Masterminds/semver v1.5.0 - github.com/blang/semver v3.5.1+incompatible github.com/cheggaaa/pb/v3 v3.0.4 github.com/fatih/color v1.9.0 // indirect github.com/golang/protobuf v1.3.2 // indirect @@ -16,6 +14,7 @@ require ( github.com/onsi/ginkgo v1.11.0 // indirect github.com/onsi/gomega v1.9.0 github.com/sclevine/spec v1.4.0 + github.com/ulikunitz/xz v0.5.7 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect golang.org/x/text v0.3.2 // indirect diff --git a/go.sum b/go.sum index 4c2f38c1..fc4ef856 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,9 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/MasterMinds/semver v1.5.0 h1:o48A/g5pIW8F7DsAw/uxspF/Yi1BNgVoSd2LnCiEKIU= -github.com/MasterMinds/semver v1.5.0/go.mod h1:VgTz+o1W0Y0VH2fNMYqTNX5yF13A7uet9xzz3Iqu2pY= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= -github.com/blang/semver v1.1.0 h1:ol1rO7QQB5uy7umSNV7VAmLugfLRD+17sYJujRNYPhg= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cheggaaa/pb/v3 v3.0.4 h1:QZEPYOj2ix6d5oEg63fbHmpolrnNiwjUsk+h74Yt4bM= github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= @@ -44,12 +39,12 @@ github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= -github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= +github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= +github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/vacation/vacation.go b/vacation/vacation.go index 0c23009d..05ab0902 100644 --- a/vacation/vacation.go +++ b/vacation/vacation.go @@ -7,6 +7,8 @@ import ( "io" "os" "path/filepath" + + "github.com/ulikunitz/xz" ) type TarArchive struct { @@ -17,6 +19,10 @@ type TarGzipArchive struct { reader io.Reader } +type TarXZArchive struct { + reader io.Reader +} + func NewTarArchive(inputReader io.Reader) TarArchive { return TarArchive{reader: inputReader} } @@ -25,6 +31,10 @@ func NewTarGzipArchive(inputReader io.Reader) TarGzipArchive { return TarGzipArchive{reader: inputReader} } +func NewTarXZArchive(inputReader io.Reader) TarXZArchive { + return TarXZArchive{reader: inputReader} +} + func (ta TarArchive) Decompress(destination string) error { tarReader := tar.NewReader(ta.reader) for { @@ -75,8 +85,17 @@ func (ta TarArchive) Decompress(destination string) error { func (gz TarGzipArchive) Decompress(destination string) error { gzr, err := gzip.NewReader(gz.reader) if err != nil { - return fmt.Errorf("failed to create gzip reader: %s", err) + return fmt.Errorf("failed to create gzip reader: %w", err) } return NewTarArchive(gzr).Decompress(destination) } + +func (txz TarXZArchive) Decompress(destination string) error { + xzr, err := xz.NewReader(txz.reader) + if err != nil { + return fmt.Errorf("failed to create xz reader: %w", err) + } + + return NewTarArchive(xzr).Decompress(destination) +} diff --git a/vacation/vacation_test.go b/vacation/vacation_test.go index 588b3a33..ef06a9d7 100644 --- a/vacation/vacation_test.go +++ b/vacation/vacation_test.go @@ -12,6 +12,7 @@ import ( "github.com/cloudfoundry/packit/vacation" "github.com/sclevine/spec" + "github.com/ulikunitz/xz" . "github.com/onsi/gomega" ) @@ -250,4 +251,94 @@ func testVacation(t *testing.T, context spec.G, it spec.S) { }) }) }) + + context("TarXZArchive.Decompress", func() { + var ( + tempDir string + tarXZArchive vacation.TarXZArchive + ) + + it.Before(func() { + var err error + tempDir, err = ioutil.TempDir("", "vacation") + Expect(err).NotTo(HaveOccurred()) + + buffer := bytes.NewBuffer(nil) + xzw, err := xz.NewWriter(buffer) + Expect(err).NotTo(HaveOccurred()) + + tw := tar.NewWriter(xzw) + + Expect(tw.WriteHeader(&tar.Header{Name: "some-dir", Mode: 0755, Typeflag: tar.TypeDir})).To(Succeed()) + _, err = tw.Write(nil) + Expect(err).NotTo(HaveOccurred()) + + Expect(tw.WriteHeader(&tar.Header{Name: filepath.Join("some-dir", "some-other-dir"), Mode: 0755, Typeflag: tar.TypeDir})).To(Succeed()) + _, err = tw.Write(nil) + Expect(err).NotTo(HaveOccurred()) + + nestedFile := filepath.Join("some-dir", "some-other-dir", "some-file") + Expect(tw.WriteHeader(&tar.Header{Name: nestedFile, Mode: 0755, Size: int64(len(nestedFile))})).To(Succeed()) + _, err = tw.Write([]byte(nestedFile)) + Expect(err).NotTo(HaveOccurred()) + + for _, file := range []string{"first", "second", "third"} { + Expect(tw.WriteHeader(&tar.Header{Name: file, Mode: 0755, Size: int64(len(file))})).To(Succeed()) + _, err = tw.Write([]byte(file)) + Expect(err).NotTo(HaveOccurred()) + } + + Expect(tw.WriteHeader(&tar.Header{Name: "symlink", Mode: 0777, Size: int64(0), Typeflag: tar.TypeSymlink, Linkname: "first"})).To(Succeed()) + _, err = tw.Write([]byte{}) + Expect(err).NotTo(HaveOccurred()) + + Expect(tw.Close()).To(Succeed()) + Expect(xzw.Close()).To(Succeed()) + + tarXZArchive = vacation.NewTarXZArchive(bytes.NewReader(buffer.Bytes())) + + }) + + it.After(func() { + Expect(os.RemoveAll(tempDir)).To(Succeed()) + }) + + it("downloads the dependency and unpackages it into the path", func() { + var err error + err = tarXZArchive.Decompress(tempDir) + Expect(err).ToNot(HaveOccurred()) + + files, err := filepath.Glob(fmt.Sprintf("%s/*", tempDir)) + Expect(err).NotTo(HaveOccurred()) + Expect(files).To(ConsistOf([]string{ + filepath.Join(tempDir, "first"), + filepath.Join(tempDir, "second"), + filepath.Join(tempDir, "third"), + filepath.Join(tempDir, "some-dir"), + filepath.Join(tempDir, "symlink"), + })) + + info, err := os.Stat(filepath.Join(tempDir, "first")) + Expect(err).NotTo(HaveOccurred()) + Expect(info.Mode()).To(Equal(os.FileMode(0755))) + + Expect(filepath.Join(tempDir, "some-dir", "some-other-dir")).To(BeADirectory()) + Expect(filepath.Join(tempDir, "some-dir", "some-other-dir", "some-file")).To(BeARegularFile()) + + data, err := ioutil.ReadFile(filepath.Join(tempDir, "symlink")) + Expect(err).NotTo(HaveOccurred()) + Expect(data).To(Equal([]byte(`first`))) + }) + + context("failure cases", func() { + context("when it fails to create a xz reader", func() { + it("returns an error", func() { + readyArchive := vacation.NewTarXZArchive(bytes.NewBuffer([]byte(`something`))) + + err := readyArchive.Decompress(tempDir) + Expect(err).To(MatchError(ContainSubstring("failed to create xz reader"))) + }) + }) + }) + }) }