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

Store download cache in versioned directories #231

Merged
merged 1 commit into from
Jul 5, 2019
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
25 changes: 18 additions & 7 deletions downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ import (
"github.com/buildpack/pack/logging"
)

const (
cacheDirPrefix = "c"
cacheVersion = "1"
)

type Downloader struct {
logger logging.Logger
cacheDir string
logger logging.Logger
baseCacheDir string
}

var schemeRegexp = regexp.MustCompile(`^.+://.*`)

func NewDownloader(logger logging.Logger, cacheDir string) *Downloader {
func NewDownloader(logger logging.Logger, baseCacheDir string) *Downloader {
return &Downloader{
logger: logger,
cacheDir: cacheDir,
logger: logger,
baseCacheDir: baseCacheDir,
}
}

Expand Down Expand Up @@ -65,11 +70,13 @@ func (d *Downloader) handleFile(path string) (string, error) {
}

func (d *Downloader) handleHTTP(uri string) (string, error) {
if err := os.MkdirAll(d.cacheDir, 0744); err != nil {
cacheDir := d.versionedCacheDir()

if err := os.MkdirAll(cacheDir, 0744); err != nil {
return "", err
}

cachePath := filepath.Join(d.cacheDir, fmt.Sprintf("%x", sha256.Sum256([]byte(uri))))
cachePath := filepath.Join(cacheDir, fmt.Sprintf("%x", sha256.Sum256([]byte(uri))))
tgzFile := cachePath + ".tgz"

etagFile := cachePath + ".etag"
Expand Down Expand Up @@ -141,6 +148,10 @@ func (d *Downloader) downloadAsStream(uri string, etag string) (io.ReadCloser, s
return nil, "", fmt.Errorf("could not download from %q, code http status %d", uri, resp.StatusCode)
}

func (d *Downloader) versionedCacheDir() string {
return filepath.Join(d.baseCacheDir, cacheDirPrefix+cacheVersion)
}

func fileExists(file string) (bool, error) {
_, err := os.Stat(file)
if err != nil {
Expand Down
214 changes: 113 additions & 101 deletions downloader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,144 +23,156 @@ func TestDownloader(t *testing.T) {
func testDownloader(t *testing.T, when spec.G, it spec.S) {
when("#Download", func() {
var (
err error
tmpDir string
tgz string
cacheDir string
subject *Downloader
cacheDir string
err error
originalWd string
subject *Downloader
tgz string
tmpDir string
)

it.Before(func() {
tmpDir, err = ioutil.TempDir("", "")
tmpDir, err = ioutil.TempDir("", "test-downloader")
h.AssertNil(t, err)

cacheDir, err = ioutil.TempDir("", "")
cacheDir, err = ioutil.TempDir(tmpDir, "cache")
h.AssertNil(t, err)

subject = NewDownloader(mocks.NewMockLogger(ioutil.Discard), cacheDir)

tgz = h.CreateTgz(t, filepath.Join("testdata", "downloader", "dirA"), "./", 0777)
testDataDir := filepath.Join("testdata", "downloader", "dirA")
h.AssertNil(t, os.MkdirAll(filepath.Join(tmpDir, testDataDir), 0777))
h.RecursiveCopy(t, testDataDir, filepath.Join(tmpDir, testDataDir))

tgz = filepath.Join(tmpDir, "dirA.tgz")
err = os.Rename(h.CreateTgz(t, testDataDir, "./", 0777), tgz)
h.AssertNil(t, err)

originalWd, err = os.Getwd()
h.AssertNil(t, err)

err := os.Chdir(tmpDir)
h.AssertNil(t, err)
})

it.After(func() {
h.AssertNil(t, os.RemoveAll(tgz))
h.AssertNil(t, os.Chdir(originalWd))
h.AssertNil(t, os.RemoveAll(tmpDir))
h.AssertNil(t, os.RemoveAll(cacheDir))
})

it("download from a relative directory", func() {
out, err := subject.Download(filepath.Join("testdata", "downloader", "dirA"))
h.AssertNil(t, err)
h.AssertNotEq(t, out, "")
h.AssertDirContainsFileWithContents(t, out, "file.txt", "some file contents")
when("is path", func() {
when("is absolute", func() {
it("downloads from directory", func() {
absPath, err := filepath.Abs(filepath.Join("testdata", "downloader", "dirA"))
h.AssertNil(t, err)

out, err := subject.Download(absPath)
h.AssertNil(t, err)
h.AssertNotEq(t, out, "")
h.AssertDirContainsFileWithContents(t, out, "file.txt", "some file contents")
})

it("downloads from tgz", func() {
absPath, err := filepath.Abs(tgz)
h.AssertNil(t, err)

out, err := subject.Download(absPath)
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
})

when("is relative", func() {
it("downloads from directory", func() {
out, err := subject.Download(filepath.Join("testdata", "downloader", "dirA"))
h.AssertNil(t, err)
h.AssertNotEq(t, out, "")
h.AssertDirContainsFileWithContents(t, out, "file.txt", "some file contents")
})

it("downloads from tgz", func() {
out, err := subject.Download(filepath.Base(tgz))
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
})
})

when("relative", func() {
var (
ogWd string
err error
)
when("is uri", func() {
it("downloads from a 'file://' URI directory", func() {
absPath, err := filepath.Abs(filepath.Join("testdata", "downloader", "dirA"))
h.AssertNil(t, err)

it.Before(func() {
ogWd, err = os.Getwd()
uri, err := paths.FilePathToUri(absPath)
h.AssertNil(t, err)

err := os.Chdir(filepath.Dir(tgz))
out, err := subject.Download(uri)
h.AssertNil(t, err)
h.AssertNotEq(t, out, "")
h.AssertDirContainsFileWithContents(t, out, "file.txt", "some file contents")
})

it.After(func() {
err := os.Chdir(ogWd)
it("downloads from a 'file://' URI tgz", func() {
absPath, err := filepath.Abs(tgz)
h.AssertNil(t, err)

uri, err := paths.FilePathToUri(absPath)
h.AssertNil(t, err)
})

it("download from tgz", func() {
out, err := subject.Download(filepath.Base(tgz))
out, err := subject.Download(uri)
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
})

it("download from an absolute directory", func() {
absPath, err := filepath.Abs(filepath.Join("testdata", "downloader", "dirA"))
h.AssertNil(t, err)

out, err := subject.Download(absPath)
h.AssertNil(t, err)
h.AssertNotEq(t, out, "")
h.AssertDirContainsFileWithContents(t, out, "file.txt", "some file contents")
})

it("download from an absolute tgz", func() {
absPath, err := filepath.Abs(tgz)
h.AssertNil(t, err)

out, err := subject.Download(absPath)
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})

it("download from a 'file://' URI directory", func() {
absPath, err := filepath.Abs(filepath.Join("testdata", "downloader", "dirA"))
h.AssertNil(t, err)

uri, err := paths.FilePathToUri(absPath)
h.AssertNil(t, err)

out, err := subject.Download(uri)
h.AssertNil(t, err)
h.AssertNotEq(t, out, "")
h.AssertDirContainsFileWithContents(t, out, "file.txt", "some file contents")
})

it("download from a 'file://' URI tgz", func() {
absPath, err := filepath.Abs(tgz)
h.AssertNil(t, err)

uri, err := paths.FilePathToUri(absPath)
h.AssertNil(t, err)

out, err := subject.Download(uri)
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
it("downloads from a 'http(s)://' URI tgz", func() {
server := ghttp.NewServer()
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, tgz)
})
defer server.Close()

it("download from a 'http(s)://' URI tgz", func() {
server := ghttp.NewServer()
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, tgz)
out, err := subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
defer server.Close()

out, err := subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
it("caches to versioned directory", func() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only added test. All other changes here are just clean up/organizational.

server := ghttp.NewServer()
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, tgz)
})
defer server.Close()

it("use cache from a 'http(s)://' URI tgz", func() {
server := ghttp.NewServer()
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("ETag", "A")
http.ServeFile(w, r, tgz)
})
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(304)
out, err := subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertContains(t, out, filepath.Join(cacheDir, cacheDirPrefix+cacheVersion))
})
defer server.Close()

out, err := subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
it("uses cache from a 'http(s)://' URI tgz", func() {
server := ghttp.NewServer()
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("ETag", "A")
http.ServeFile(w, r, tgz)
})
server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(304)
})
defer server.Close()

out, err := subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))

out, err = subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
out, err = subject.Download(server.URL() + "/downloader/somefile.tgz")
h.AssertNil(t, err)
h.AssertMatch(t, out, `\.tgz$`)
h.AssertOnTarEntry(t, out, "file.txt", h.ContentEquals("some file contents"))
})
})
})
}
25 changes: 16 additions & 9 deletions testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,15 +332,22 @@ func RecursiveCopy(t *testing.T, src, dst string) {
AssertNil(t, err)
for _, fi := range fis {
if fi.Mode().IsRegular() {
srcFile, err := os.Open(filepath.Join(src, fi.Name()))
AssertNil(t, err)
dstFile, err := os.OpenFile(filepath.Join(dst, fi.Name()), os.O_RDWR|os.O_CREATE|os.O_TRUNC, fi.Mode())
AssertNil(t, err)
_, err = io.Copy(dstFile, srcFile)
AssertNil(t, err)
modifiedtime := time.Time{}
err = os.Chtimes(filepath.Join(dst, fi.Name()), modifiedtime, modifiedtime)
AssertNil(t, err)
func() {
srcFile, err := os.Open(filepath.Join(src, fi.Name()))
AssertNil(t, err)
defer srcFile.Close()

dstFile, err := os.OpenFile(filepath.Join(dst, fi.Name()), os.O_RDWR|os.O_CREATE|os.O_TRUNC, fi.Mode())
AssertNil(t, err)
defer dstFile.Close()

_, err = io.Copy(dstFile, srcFile)
AssertNil(t, err)

modifiedtime := time.Time{}
err = os.Chtimes(filepath.Join(dst, fi.Name()), modifiedtime, modifiedtime)
AssertNil(t, err)
}()
}
if fi.IsDir() {
err = os.Mkdir(filepath.Join(dst, fi.Name()), fi.Mode())
Expand Down