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

Include symlinks in snapshots when migrating disks #2687

Merged
merged 2 commits into from
Jun 8, 2017
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
19 changes: 11 additions & 8 deletions client/allocdir/alloc_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,29 @@ func (d *AllocDir) Snapshot(w io.Writer) error {
defer tw.Close()

walkFn := func(path string, fileInfo os.FileInfo, err error) error {
// Ignore if the file is a symlink
if fileInfo.Mode() == os.ModeSymlink {
return nil
}

// Include the path of the file name relative to the alloc dir
// so that we can put the files in the right directories
relPath, err := filepath.Rel(d.AllocDir, path)
if err != nil {
return err
}
hdr, err := tar.FileInfoHeader(fileInfo, "")
link := ""
if fileInfo.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(path)
if err != nil {
return fmt.Errorf("error reading symlink: %v", err)
}
link = target
}
hdr, err := tar.FileInfoHeader(fileInfo, link)
if err != nil {
return fmt.Errorf("error creating file header: %v", err)
}
hdr.Name = relPath
tw.WriteHeader(hdr)

// If it's a directory we just write the header into the tar
if fileInfo.IsDir() {
// If it's a directory or symlink we just write the header into the tar
if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) {
return nil
}

Expand Down
20 changes: 19 additions & 1 deletion client/allocdir/alloc_dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,23 @@ func TestAllocDir_Snapshot(t *testing.T) {
t.Fatalf("Couldn't write file to shared directory: %v", err)
}

// Write a symlink to the shared dir
link := "qux"
if err := os.Symlink("foo", filepath.Join(d.SharedDir, "data", link)); err != nil {
t.Fatalf("Couldn't write symlink to shared directory: %v", err)
}

// Write a file to the task local
exp = []byte{'b', 'a', 'r'}
file1 := "lol"
if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file1), exp, 0666); err != nil {
t.Fatalf("couldn't write to task local directory: %v", err)
t.Fatalf("couldn't write file to task local directory: %v", err)
}

// Write a symlink to the task local
link1 := "baz"
if err := os.Symlink("bar", filepath.Join(td1.LocalDir, link1)); err != nil {
t.Fatalf("couldn't write symlink to task local dirctory :%v", err)
}

var b bytes.Buffer
Expand All @@ -180,6 +192,7 @@ func TestAllocDir_Snapshot(t *testing.T) {

tr := tar.NewReader(&b)
var files []string
var links []string
for {
hdr, err := tr.Next()
if err != nil && err != io.EOF {
Expand All @@ -190,12 +203,17 @@ func TestAllocDir_Snapshot(t *testing.T) {
}
if hdr.Typeflag == tar.TypeReg {
files = append(files, hdr.FileInfo().Name())
} else if hdr.Typeflag == tar.TypeSymlink {
links = append(links, hdr.FileInfo().Name())
}
}

if len(files) != 2 {
t.Fatalf("bad files: %#v", files)
}
if len(links) != 2 {
t.Fatalf("bad links: %#v", links)
}
}

func TestAllocDir_Move(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1851,6 +1851,13 @@ func (c *Client) unarchiveAllocDir(resp io.ReadCloser, allocID string, pathToAll
os.MkdirAll(filepath.Join(pathToAllocDir, hdr.Name), os.FileMode(hdr.Mode))
continue
}
// If the header is for a symlink we create the symlink
if hdr.Typeflag == tar.TypeSymlink {
if err = os.Symlink(hdr.Linkname, filepath.Join(pathToAllocDir, hdr.Name)); err != nil {
c.logger.Printf("[ERR] client: error creating symlink: %v", err)
}
continue
}
// If the header is a file, we write to a file
if hdr.Typeflag == tar.TypeReg {
f, err := os.Create(filepath.Join(pathToAllocDir, hdr.Name))
Expand Down
34 changes: 26 additions & 8 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,27 +939,37 @@ func TestClient_UnarchiveAllocDir(t *testing.T) {
t.Fatalf("err: %v", err)
}
f.Close()
if err := os.Symlink("bar", filepath.Join(dir, "foo", "baz")); err != nil {
t.Fatalf("err: %v", err)
}
linkInfo, err := os.Lstat(filepath.Join(dir, "foo", "baz"))
if err != nil {
t.Fatalf("err: %v", err)
}

buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)

walkFn := func(path string, fileInfo os.FileInfo, err error) error {
// Ignore if the file is a symlink
if fileInfo.Mode() == os.ModeSymlink {
return nil
}

// Include the path of the file name relative to the alloc dir
// so that we can put the files in the right directories
hdr, err := tar.FileInfoHeader(fileInfo, "")
link := ""
if fileInfo.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(path)
if err != nil {
return fmt.Errorf("error reading symlink: %v", err)
}
link = target
}
hdr, err := tar.FileInfoHeader(fileInfo, link)
if err != nil {
return fmt.Errorf("error creating file header: %v", err)
}
hdr.Name = fileInfo.Name()
tw.WriteHeader(hdr)

// If it's a directory we just write the header into the tar
if fileInfo.IsDir() {
// If it's a directory or symlink we just write the header into the tar
if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) {
return nil
}

Expand Down Expand Up @@ -1016,4 +1026,12 @@ func TestClient_UnarchiveAllocDir(t *testing.T) {
if fi1.Mode() != fInfo.Mode() {
t.Fatalf("mode: %v", fi1.Mode())
}

fi2, err := os.Lstat(filepath.Join(dir1, "baz"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi2.Mode() != linkInfo.Mode() {
t.Fatalf("mode: %v", fi2.Mode())
}
}