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

Add Windows support to cacheddownloader #6

Merged
merged 5 commits into from
Feb 27, 2015
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
2 changes: 1 addition & 1 deletion cached_downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type CachedDownloader interface {
}

func NoopTransform(source, destination string) (int64, error) {
err := os.Rename(source, destination)
err := replace(source, destination)
if err != nil {
return 0, err
}
Expand Down
3 changes: 3 additions & 0 deletions cached_downloader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ var _ = Describe("File cache", func() {
))

url, _ = Url.Parse(server.URL() + "/A")
// windows timer increments every 15.6ms, without the sleep
// A, B & C will sometimes have the same timestamp
time.Sleep(16 * time.Millisecond)
cache.Fetch(url, "A", NoopTransform, cancelChan)
})

Expand Down
4 changes: 2 additions & 2 deletions file_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (e *fileCacheEntry) readCloser() (*CachedFile, error) {
return readCloser, nil
}

func (c *FileCache) Add(cacheKey string, sourcePath string, size int64, cachingInfo CachingInfoType) (*CachedFile, error) {
func (c *FileCache) Add(cacheKey, sourcePath string, size int64, cachingInfo CachingInfoType) (*CachedFile, error) {
lock.Lock()
defer lock.Unlock()

Expand All @@ -105,7 +105,7 @@ func (c *FileCache) Add(cacheKey string, sourcePath string, size int64, cachingI
uniqueName := fmt.Sprintf("%s-%d-%d", cacheKey, time.Now().UnixNano(), c.seq)
cachePath := filepath.Join(c.cachedPath, uniqueName)

err := os.Rename(sourcePath, cachePath)
err := replace(sourcePath, cachePath)
if err != nil {
return nil, err
}
Expand Down
11 changes: 11 additions & 0 deletions replace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// +build !windows

package cacheddownloader

import "os"

// if you are wondering why we have this function, see `replace'
// implementation in replace_windows.go
func replace(src, dst string) error {
return os.Rename(src, dst)
}
45 changes: 45 additions & 0 deletions replace_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cacheddownloader

import (
"syscall"
"unsafe"
)

// Replaces `dst' with `src' atomically. Under linux we only have to
// call os.Rename(), on windows os.Rename() will error if the
// destination exists already. The replace function serves as a
// unified interface on both platforms.
func replace(src, dst string) error {
kernel32, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
return err
}
defer syscall.FreeLibrary(kernel32)
moveFileExUnicode, err := syscall.GetProcAddress(kernel32, "MoveFileExW")
if err != nil {
return err
}

srcString, err := syscall.UTF16PtrFromString(src)
if err != nil {
return err
}

dstString, err := syscall.UTF16PtrFromString(dst)
if err != nil {
return err
}

srcPtr := uintptr(unsafe.Pointer(srcString))
dstPtr := uintptr(unsafe.Pointer(dstString))

MOVEFILE_REPLACE_EXISTING := 0x1
flag := uintptr(MOVEFILE_REPLACE_EXISTING)

_, _, callErr := syscall.Syscall(uintptr(moveFileExUnicode), 3, srcPtr, dstPtr, flag)
if callErr != 0 {
return callErr
}

return nil
}
6 changes: 6 additions & 0 deletions tar_transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func transformZipToTar(path, destPath string) (int64, error) {
if err != nil {
return 0, err
}
defer dest.Close()

zr, err := zip.OpenReader(path)
if err != nil {
Expand All @@ -136,6 +137,11 @@ func transformZipToTar(path, destPath string) (int64, error) {
return 0, err
}

err = zr.Close()
if err != nil {
return 0, err
}

err = os.Remove(path)
if err != nil {
return 0, err
Expand Down
68 changes: 68 additions & 0 deletions tar_transformer_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cacheddownloader_test

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

"github.com/pivotal-golang/archiver/extractor/test_helper"
. "github.com/pivotal-golang/cacheddownloader"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("TarTransformer", func() {
var (
scratch string

sourcePath string
destinationPath string

transformedSize int64
transformErr error
)

archiveFiles := []test_helper.ArchiveFile{
{Name: "some-file", Body: "some-contents"},
}

BeforeEach(func() {
var err error

scratch, err = ioutil.TempDir("", "tar-transformer-scratch")
Expect(err).ShouldNot(HaveOccurred())

destinationFile, err := ioutil.TempFile("", "destination")
Expect(err).ShouldNot(HaveOccurred())

err = destinationFile.Close()
Expect(err).ShouldNot(HaveOccurred())

destinationPath = destinationFile.Name()
})

AfterEach(func() {
err := os.RemoveAll(scratch)
Expect(err).ShouldNot(HaveOccurred())
})

JustBeforeEach(func() {
transformedSize, transformErr = TarTransform(sourcePath, destinationPath)
})

Context("when the file is a .zip", func() {
BeforeEach(func() {
sourcePath = filepath.Join(scratch, "file.zip")

test_helper.CreateZipArchive(sourcePath, archiveFiles)
})

It("closes the tarfile", func() {
// On Windows, you can't remove files that are still open. On Linux, you can.
err := os.Remove(destinationPath)

Expect(err).ShouldNot(HaveOccurred())
})
})
})