preserve specified destination path

This commit is contained in:
juanvallejo 2017-08-23 14:46:17 -04:00
parent 8eab275cce
commit 2371accf0c
No known key found for this signature in database
GPG Key ID: 7D2C958002D6448D
2 changed files with 127 additions and 10 deletions

View File

@ -18,6 +18,7 @@ package cmd
import (
"archive/tar"
"bytes"
"errors"
"io"
"io/ioutil"
@ -134,11 +135,44 @@ func runCopy(f cmdutil.Factory, cmd *cobra.Command, out, cmderr io.Writer, args
return cmdutil.UsageErrorf(cmd, "One of src or dest must be a remote file specification")
}
// checkDestinationIsDir receives a destination fileSpec and
// determines if the provided destination path exists on the
// pod. If the destination path does not exist or is _not_ a
// directory, an error is returned with the exit code received.
func checkDestinationIsDir(dest fileSpec, f cmdutil.Factory, cmd *cobra.Command) error {
options := &ExecOptions{
StreamOptions: StreamOptions{
Out: bytes.NewBuffer([]byte{}),
Err: bytes.NewBuffer([]byte{}),
Namespace: dest.PodNamespace,
PodName: dest.PodName,
},
Command: []string{"test", "-d", dest.File},
Executor: &DefaultRemoteExecutor{},
}
return execute(f, cmd, options)
}
func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer, src, dest fileSpec) error {
reader, writer := io.Pipe()
// strip trailing slash (if any)
if strings.HasSuffix(string(dest.File[len(dest.File)-1]), "/") {
dest.File = dest.File[:len(dest.File)-1]
}
if err := checkDestinationIsDir(dest, f, cmd); err == nil {
// If no error, dest.File was found to be a directory.
// Copy specified src into it
dest.File = dest.File + "/" + path.Base(src.File)
}
go func() {
defer writer.Close()
err := makeTar(src.File, writer)
err := makeTar(src.File, dest.File, writer)
cmdutil.CheckErr(err)
}()
@ -192,17 +226,18 @@ func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, d
return untarAll(reader, dest.File, prefix)
}
func makeTar(filepath string, writer io.Writer) error {
func makeTar(srcPath, destPath string, writer io.Writer) error {
// TODO: use compression here?
tarWriter := tar.NewWriter(writer)
defer tarWriter.Close()
filepath = path.Clean(filepath)
return recursiveTar(path.Dir(filepath), path.Base(filepath), tarWriter)
srcPath = path.Clean(srcPath)
destPath = path.Clean(destPath)
return recursiveTar(path.Dir(srcPath), path.Base(srcPath), path.Dir(destPath), path.Base(destPath), tarWriter)
}
func recursiveTar(base, file string, tw *tar.Writer) error {
filepath := path.Join(base, file)
func recursiveTar(srcBase, srcFile, destBase, destFile string, tw *tar.Writer) error {
filepath := path.Join(srcBase, srcFile)
stat, err := os.Stat(filepath)
if err != nil {
return err
@ -213,7 +248,7 @@ func recursiveTar(base, file string, tw *tar.Writer) error {
return err
}
for _, f := range files {
if err := recursiveTar(base, path.Join(file, f.Name()), tw); err != nil {
if err := recursiveTar(srcBase, path.Join(srcFile, f.Name()), destBase, path.Join(destFile, f.Name()), tw); err != nil {
return err
}
}
@ -223,7 +258,7 @@ func recursiveTar(base, file string, tw *tar.Writer) error {
if err != nil {
return err
}
hdr.Name = file
hdr.Name = destFile
if err := tw.WriteHeader(hdr); err != nil {
return err
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package cmd
import (
"archive/tar"
"bytes"
"io"
"io/ioutil"
@ -24,6 +25,7 @@ import (
"os/exec"
"path"
"path/filepath"
"strings"
"testing"
)
@ -129,7 +131,7 @@ func TestTarUntar(t *testing.T) {
data: "bazblahfoo",
},
{
name: "some/other/directory",
name: "some/other/directory/",
data: "with more data here",
},
{
@ -157,7 +159,7 @@ func TestTarUntar(t *testing.T) {
}
writer := &bytes.Buffer{}
if err := makeTar(dir, writer); err != nil {
if err := makeTar(dir, dir2, writer); err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -292,3 +294,83 @@ func TestCopyToLocalFileOrDir(t *testing.T) {
}
}
func TestTarDestinationName(t *testing.T) {
dir, err := ioutil.TempDir(os.TempDir(), "input")
dir2, err2 := ioutil.TempDir(os.TempDir(), "output")
if err != nil || err2 != nil {
t.Errorf("unexpected error: %v | %v", err, err2)
t.FailNow()
}
defer func() {
if err := os.RemoveAll(dir); err != nil {
t.Errorf("Unexpected error cleaning up: %v", err)
}
if err := os.RemoveAll(dir2); err != nil {
t.Errorf("Unexpected error cleaning up: %v", err)
}
}()
files := []struct {
name string
data string
}{
{
name: "foo",
data: "foobarbaz",
},
{
name: "dir/blah",
data: "bazblahfoo",
},
{
name: "some/other/directory",
data: "with more data here",
},
{
name: "blah",
data: "same file name different data",
},
}
// ensure files exist on disk
for _, file := range files {
filepath := path.Join(dir, file.name)
if err := os.MkdirAll(path.Dir(filepath), 0755); err != nil {
t.Errorf("unexpected error: %v", err)
t.FailNow()
}
f, err := os.Create(filepath)
if err != nil {
t.Errorf("unexpected error: %v", err)
t.FailNow()
}
defer f.Close()
if _, err := io.Copy(f, bytes.NewBuffer([]byte(file.data))); err != nil {
t.Errorf("unexpected error: %v", err)
t.FailNow()
}
}
reader, writer := io.Pipe()
go func() {
if err := makeTar(dir, dir2, writer); err != nil {
t.Errorf("unexpected error: %v", err)
}
}()
tarReader := tar.NewReader(reader)
for {
hdr, err := tarReader.Next()
if err == io.EOF {
break
} else if err != nil {
t.Errorf("unexpected error: %v", err)
t.FailNow()
}
if !strings.HasPrefix(hdr.Name, path.Base(dir2)) {
t.Errorf("expected %q as destination filename prefix, saw: %q", path.Base(dir2), hdr.Name)
}
}
}