Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vacation: handle compressed non-tar archives #258

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion postal/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ func (s Service) Deliver(dependency Dependency, cnbPath, layerPath, platformPath

validatedReader := cargo.NewValidatedReader(bundle, dependency.SHA256)

name := filepath.Base(dependency.URI)
name := dependency.Name
if name == "" {
name = filepath.Base(dependency.URI)
}
err = vacation.NewArchive(validatedReader).WithName(name).StripComponents(dependency.StripComponents).Decompress(layerPath)
if err != nil {
return err
Expand Down
46 changes: 0 additions & 46 deletions postal/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,29 +598,6 @@ version = "this is super not semver"
})
})

context("when the file contents are malformed", func() {
it.Before(func() {
buffer := bytes.NewBuffer(nil)
gzipWriter := gzip.NewWriter(buffer)

_, err := gzipWriter.Write([]byte("something"))
Expect(err).NotTo(HaveOccurred())

Expect(gzipWriter.Close()).To(Succeed())

transport.DropCall.Returns.ReadCloser = io.NopCloser(buffer)

sum := sha256.Sum256(buffer.Bytes())
dependencySHA = hex.EncodeToString(sum[:])
})

it("fails to create a tar reader", func() {
err := deliver()

Expect(err).To(MatchError(ContainSubstring("failed to read tar response")))
})
})

context("when the file checksum does not match", func() {
it("fails to create a tar reader", func() {
err := service.Deliver(
Expand Down Expand Up @@ -865,29 +842,6 @@ version = "this is super not semver"
})
})

context("when the file contents are malformed", func() {
it.Before(func() {
buffer := bytes.NewBuffer(nil)
gzipWriter := gzip.NewWriter(buffer)

_, err := gzipWriter.Write([]byte("something"))
Expect(err).NotTo(HaveOccurred())

Expect(gzipWriter.Close()).To(Succeed())

transport.DropCall.Returns.ReadCloser = io.NopCloser(buffer)

sum := sha256.Sum256(buffer.Bytes())
dependencySHA = hex.EncodeToString(sum[:])
})

it("fails to create a tar reader", func() {
err := install()

Expect(err).To(MatchError(ContainSubstring("failed to read tar response")))
})
})

context("when the file checksum does not match", func() {
it("fails to create a tar reader", func() {
err := service.Install(postal.Dependency{
Expand Down
8 changes: 5 additions & 3 deletions vacation/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ func (a Archive) Decompress(destination string) error {
case "application/x-tar":
decompressor = NewTarArchive(bufferedReader).StripComponents(a.components)
case "application/gzip":
decompressor = NewTarGzipArchive(bufferedReader).StripComponents(a.components)
decompressor = NewGzipArchive(bufferedReader).StripComponents(a.components).WithName(a.name)
case "application/x-xz":
decompressor = NewTarXZArchive(bufferedReader).StripComponents(a.components)
decompressor = NewXZArchive(bufferedReader).StripComponents(a.components)
case "application/x-bzip2":
decompressor = NewTarBzip2Archive(bufferedReader).StripComponents(a.components)
decompressor = NewBzip2Archive(bufferedReader).StripComponents(a.components)
case "application/zip":
decompressor = NewZipArchive(bufferedReader).StripComponents(a.components)
case "application/x-executable":
decompressor = NewExecutable(bufferedReader, a.name)
case "text/plain; charset=utf-8", "application/jar":
destination = filepath.Join(destination, a.name)
decompressor = NewNopArchive(bufferedReader)
Expand Down
47 changes: 47 additions & 0 deletions vacation/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"archive/zip"
"bytes"
"compress/gzip"
"encoding/base64"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -346,6 +349,50 @@ func testArchive(t *testing.T, context spec.G, it spec.S) {
})
})

context("when passed the reader of an executable file", func() {
var (
archive vacation.Archive
tempDir string
// Encoding of a very small elf executable from https://github.com/mathiasbynens/small
encodedContents = []byte(`f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAGUDNgCwAAAAAAAAAAAAAADQAIAABAAAAAAAAAABAzYAAQM2ATAAAAEwAAAAFAAAAABAAAA==`)
literalContents []byte
fileName = "exe"
)

it.Before(func() {
var err error
tempDir, err = os.MkdirTemp("", "vacation")
Expect(err).NotTo(HaveOccurred())

literalContents, err = ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(encodedContents)))
Expect(err).NotTo(HaveOccurred())

archive = vacation.NewArchive(bytes.NewBuffer(literalContents)).WithName(fileName)
})

it.After(func() {
Expect(os.RemoveAll(tempDir)).To(Succeed())
})

it("writes the executable in the bin dir", func() {
err := archive.Decompress(tempDir)
Expect(err).NotTo(HaveOccurred())

content, err := os.ReadFile(filepath.Join(tempDir, "bin", fileName))
Expect(err).NotTo(HaveOccurred())
Expect(content).To(Equal(literalContents))
})

it("gives the executable execute permission", func() {
err := archive.Decompress(tempDir)
Expect(err).NotTo(HaveOccurred())

info, err := os.Stat(filepath.Join(tempDir, "bin", fileName))
Expect(err).NotTo(HaveOccurred())
Expect(info.Mode()).To(Equal(fs.FileMode(0755)))
})
})

context("when passed the reader of a text file", func() {
var (
archive vacation.Archive
Expand Down
30 changes: 30 additions & 0 deletions vacation/bzip2_archive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package vacation

import (
"compress/bzip2"
"io"
)

// A Bzip2Archive decompresses bzip2 files from an input stream.
type Bzip2Archive struct {
reader io.Reader
components int
}

// NewBzip2Archive returns a new Bzip2Archive that reads from inputReader.
func NewBzip2Archive(inputReader io.Reader) Bzip2Archive {
return Bzip2Archive{reader: inputReader}
}

// Decompress reads from Bzip2Archive and writes files into the destination
// specified.
func (tbz Bzip2Archive) Decompress(destination string) error {
return NewArchive(bzip2.NewReader(tbz.reader)).StripComponents(tbz.components).Decompress(destination)
}

// StripComponents behaves like the --strip-components flag on tar command
// removing the first n levels from the final decompression destination.
func (tbz Bzip2Archive) StripComponents(components int) Bzip2Archive {
tbz.components = components
return tbz
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ import (
. "github.com/onsi/gomega"
)

func testTarBzip2Archive(t *testing.T, context spec.G, it spec.S) {
func testBzip2Archive(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect
)

context("Decompress", func() {
var (
tempDir string
tarBzip2Archive vacation.TarBzip2Archive
bzip2Archive vacation.Bzip2Archive
)

it.Before(func() {
Expand Down Expand Up @@ -68,7 +68,7 @@ func testTarBzip2Archive(t *testing.T, context spec.G, it spec.S) {
Expect(tw.Close()).To(Succeed())
Expect(bz.Close()).To(Succeed())

tarBzip2Archive = vacation.NewTarBzip2Archive(bytes.NewReader(buffer.Bytes()))
bzip2Archive = vacation.NewBzip2Archive(bytes.NewReader(buffer.Bytes()))
})

it.After(func() {
Expand All @@ -77,7 +77,7 @@ func testTarBzip2Archive(t *testing.T, context spec.G, it spec.S) {

it("unpackages the archive into the path", func() {
var err error
err = tarBzip2Archive.Decompress(tempDir)
err = bzip2Archive.Decompress(tempDir)
Expect(err).ToNot(HaveOccurred())

files, err := filepath.Glob(fmt.Sprintf("%s/*", tempDir))
Expand All @@ -104,7 +104,7 @@ func testTarBzip2Archive(t *testing.T, context spec.G, it spec.S) {

it("unpackages the archive into the path but also strips the first component", func() {
var err error
err = tarBzip2Archive.StripComponents(1).Decompress(tempDir)
err = bzip2Archive.StripComponents(1).Decompress(tempDir)
Expect(err).ToNot(HaveOccurred())

files, err := filepath.Glob(fmt.Sprintf("%s/*", tempDir))
Expand Down
24 changes: 12 additions & 12 deletions vacation/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func ExampleTarArchive_StripComponents() {
// some-other-dir/some-file
}

func ExampleTarGzipArchive() {
func ExampleGzipArchive() {
buffer := bytes.NewBuffer(nil)
gw := gzip.NewWriter(buffer)
tw := tar.NewWriter(gw)
Expand Down Expand Up @@ -348,7 +348,7 @@ func ExampleTarGzipArchive() {
}
defer os.RemoveAll(destination)

archive := vacation.NewTarGzipArchive(bytes.NewReader(buffer.Bytes()))
archive := vacation.NewGzipArchive(bytes.NewReader(buffer.Bytes()))
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -376,7 +376,7 @@ func ExampleTarGzipArchive() {
// third
}

func ExampleTarGzipArchive_StripComponents() {
func ExampleGzipArchive_StripComponents() {
buffer := bytes.NewBuffer(nil)
gw := gzip.NewWriter(buffer)
tw := tar.NewWriter(gw)
Expand Down Expand Up @@ -411,7 +411,7 @@ func ExampleTarGzipArchive_StripComponents() {
}
defer os.RemoveAll(destination)

archive := vacation.NewTarGzipArchive(bytes.NewReader(buffer.Bytes())).StripComponents(1)
archive := vacation.NewGzipArchive(bytes.NewReader(buffer.Bytes())).StripComponents(1)
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}
Expand All @@ -436,7 +436,7 @@ func ExampleTarGzipArchive_StripComponents() {
// some-other-dir/some-file
}

func ExampleTarXZArchive() {
func ExampleXZArchive() {
buffer := bytes.NewBuffer(nil)
xw, err := xz.NewWriter(buffer)
if err != nil {
Expand Down Expand Up @@ -475,7 +475,7 @@ func ExampleTarXZArchive() {
}
defer os.RemoveAll(destination)

archive := vacation.NewTarXZArchive(bytes.NewReader(buffer.Bytes()))
archive := vacation.NewXZArchive(bytes.NewReader(buffer.Bytes()))
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -503,7 +503,7 @@ func ExampleTarXZArchive() {
// third
}

func ExampleTarXZArchive_StripComponents() {
func ExampleXZArchive_StripComponents() {
buffer := bytes.NewBuffer(nil)
xw, err := xz.NewWriter(buffer)
if err != nil {
Expand Down Expand Up @@ -542,7 +542,7 @@ func ExampleTarXZArchive_StripComponents() {
}
defer os.RemoveAll(destination)

archive := vacation.NewTarXZArchive(bytes.NewReader(buffer.Bytes())).StripComponents(1)
archive := vacation.NewXZArchive(bytes.NewReader(buffer.Bytes())).StripComponents(1)
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}
Expand All @@ -567,7 +567,7 @@ func ExampleTarXZArchive_StripComponents() {
// some-other-dir/some-file
}

func ExampleTarBzip2Archive() {
func ExampleBzip2Archive() {
buffer := bytes.NewBuffer(nil)

// Using the dsnet library because the Go compression library does not
Expand Down Expand Up @@ -611,7 +611,7 @@ func ExampleTarBzip2Archive() {
}
defer os.RemoveAll(destination)

archive := vacation.NewTarBzip2Archive(bytes.NewReader(buffer.Bytes()))
archive := vacation.NewBzip2Archive(bytes.NewReader(buffer.Bytes()))
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -639,7 +639,7 @@ func ExampleTarBzip2Archive() {
// third
}

func ExampleTarBzip2Archive_StripComponents() {
func ExampleBzip2Archive_StripComponents() {
buffer := bytes.NewBuffer(nil)

// Using the dsnet library because the Go compression library does not
Expand Down Expand Up @@ -683,7 +683,7 @@ func ExampleTarBzip2Archive_StripComponents() {
}
defer os.RemoveAll(destination)

archive := vacation.NewTarBzip2Archive(bytes.NewReader(buffer.Bytes())).StripComponents(1)
archive := vacation.NewBzip2Archive(bytes.NewReader(buffer.Bytes())).StripComponents(1)
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}
Expand Down
42 changes: 42 additions & 0 deletions vacation/executable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package vacation

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

// An Executable writes an executable files from an input stream to the bin/ directory.
type Executable struct {
reader io.Reader
name string
}

func NewExecutable(inputReader io.Reader, name string) Executable {
return Executable{reader: inputReader, name: name}
}

func (e Executable) Decompress(destination string) error {
err := os.MkdirAll(filepath.Join(destination, "bin"), 0755)
if err != nil {
return err
}

file, err := os.Create(filepath.Join(destination, "bin", e.name))
if err != nil {
return err
}
defer file.Close()

_, err = io.Copy(file, e.reader)
if err != nil {
return err
}

err = os.Chmod(filepath.Join(destination, "bin", e.name), 0755)
if err != nil {
return err
}

return nil
}
Loading