mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
This patch improve kubectl cp command from two aspects
A.support soft link better before this patch "kubectl cp" command will copy the soft link to destination as an empty regular file after this patch "kubectl cp" command will behave the same as tar command this patch improves it on both from container and to container B.fix some bugs 1.from container to host a.when copy a file ends with '/', it will cause a panic. for example, container gakki has a regular file /tmp/test, then run command kubectl cp gakki:/tmp/test/ /tmp panic happens b.when copy a file which does not exist in container, the command ends up without any error information 2.from host to container a.when run command like kubectl cp "" gakki:/tmp it will try cp current directory to container, in other words, this command works the same as kubectl cp . gakki:/tmp b.current cp command will omit an empty directory modified: pkg/kubectl/cmd/cp.go modified: pkg/kubectl/cmd/cp_test.go
This commit is contained in:
parent
7c75723867
commit
b1f85e2dfe
@ -20,6 +20,7 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -82,7 +83,10 @@ type fileSpec struct {
|
||||
File string
|
||||
}
|
||||
|
||||
var errFileSpecDoesntMatchFormat = errors.New("Filespec must match the canonical format: [[namespace/]pod:]file/path")
|
||||
var (
|
||||
errFileSpecDoesntMatchFormat = errors.New("Filespec must match the canonical format: [[namespace/]pod:]file/path")
|
||||
errFileCannotBeEmpty = errors.New("Filepath can not be empty")
|
||||
)
|
||||
|
||||
func extractFileSpec(arg string) (fileSpec, error) {
|
||||
pieces := strings.Split(arg, ":")
|
||||
@ -157,6 +161,9 @@ func checkDestinationIsDir(dest fileSpec, f cmdutil.Factory, cmd *cobra.Command)
|
||||
}
|
||||
|
||||
func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer, src, dest fileSpec) error {
|
||||
if len(src.File) == 0 {
|
||||
return errFileCannotBeEmpty
|
||||
}
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
// strip trailing slash (if any)
|
||||
@ -201,6 +208,10 @@ func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer,
|
||||
}
|
||||
|
||||
func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, dest fileSpec) error {
|
||||
if len(src.File) == 0 {
|
||||
return errFileCannotBeEmpty
|
||||
}
|
||||
|
||||
reader, outStream := io.Pipe()
|
||||
options := &ExecOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
@ -222,7 +233,7 @@ func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, d
|
||||
execute(f, cmd, options)
|
||||
}()
|
||||
prefix := getPrefix(src.File)
|
||||
|
||||
prefix = path.Clean(prefix)
|
||||
return untarAll(reader, dest.File, prefix)
|
||||
}
|
||||
|
||||
@ -238,7 +249,7 @@ func makeTar(srcPath, destPath string, writer io.Writer) error {
|
||||
|
||||
func recursiveTar(srcBase, srcFile, destBase, destFile string, tw *tar.Writer) error {
|
||||
filepath := path.Join(srcBase, srcFile)
|
||||
stat, err := os.Stat(filepath)
|
||||
stat, err := os.Lstat(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -247,28 +258,55 @@ func recursiveTar(srcBase, srcFile, destBase, destFile string, tw *tar.Writer) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
//case empty directory
|
||||
hdr, _ := tar.FileInfoHeader(stat, filepath)
|
||||
hdr.Name = destFile
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, f := range files {
|
||||
if err := recursiveTar(srcBase, path.Join(srcFile, f.Name()), destBase, path.Join(destFile, f.Name()), tw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
hdr, err := tar.FileInfoHeader(stat, filepath)
|
||||
if err != nil {
|
||||
} else if stat.Mode()&os.ModeSymlink != 0 {
|
||||
//case soft link
|
||||
hdr, _ := tar.FileInfoHeader(stat, filepath)
|
||||
target, err := os.Readlink(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hdr.Linkname = target
|
||||
hdr.Name = destFile
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
//case regular file or other file type like pipe
|
||||
hdr, err := tar.FileInfoHeader(stat, filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Name = destFile
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
_, err = io.Copy(tw, f)
|
||||
return err
|
||||
}
|
||||
hdr.Name = destFile
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(tw, f)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func untarAll(reader io.Reader, destFile, prefix string) error {
|
||||
@ -285,6 +323,7 @@ func untarAll(reader io.Reader, destFile, prefix string) error {
|
||||
break
|
||||
}
|
||||
entrySeq++
|
||||
mode := header.FileInfo().Mode()
|
||||
outFileName := path.Join(destFile, header.Name[len(prefix):])
|
||||
baseName := path.Dir(outFileName)
|
||||
if err := os.MkdirAll(baseName, 0755); err != nil {
|
||||
@ -308,15 +347,27 @@ func untarAll(reader io.Reader, destFile, prefix string) error {
|
||||
outFileName = filepath.Join(outFileName, path.Base(header.Name))
|
||||
}
|
||||
}
|
||||
outFile, err := os.Create(outFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||
return err
|
||||
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
err := os.Symlink(header.Linkname, outFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
outFile, err := os.Create(outFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
io.Copy(outFile, tarReader)
|
||||
}
|
||||
}
|
||||
|
||||
if entrySeq == -1 {
|
||||
//if no file was copied
|
||||
errInfo := fmt.Sprintf("error: %s no such file or directory", prefix)
|
||||
return errors.New(errInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,13 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type FileType int
|
||||
|
||||
const (
|
||||
RegularFile FileType = 0
|
||||
SymLink FileType = 1
|
||||
)
|
||||
|
||||
func TestExtractFileSpec(t *testing.T) {
|
||||
tests := []struct {
|
||||
spec string
|
||||
@ -119,24 +126,34 @@ func TestTarUntar(t *testing.T) {
|
||||
}()
|
||||
|
||||
files := []struct {
|
||||
name string
|
||||
data string
|
||||
name string
|
||||
data string
|
||||
fileType FileType
|
||||
}{
|
||||
{
|
||||
name: "foo",
|
||||
data: "foobarbaz",
|
||||
name: "foo",
|
||||
data: "foobarbaz",
|
||||
fileType: RegularFile,
|
||||
},
|
||||
{
|
||||
name: "dir/blah",
|
||||
data: "bazblahfoo",
|
||||
name: "dir/blah",
|
||||
data: "bazblahfoo",
|
||||
fileType: RegularFile,
|
||||
},
|
||||
{
|
||||
name: "some/other/directory/",
|
||||
data: "with more data here",
|
||||
name: "some/other/directory/",
|
||||
data: "with more data here",
|
||||
fileType: RegularFile,
|
||||
},
|
||||
{
|
||||
name: "blah",
|
||||
data: "same file name different data",
|
||||
name: "blah",
|
||||
data: "same file name different data",
|
||||
fileType: RegularFile,
|
||||
},
|
||||
{
|
||||
name: "gakki",
|
||||
data: "/tmp/gakki",
|
||||
fileType: SymLink,
|
||||
},
|
||||
}
|
||||
|
||||
@ -146,20 +163,32 @@ func TestTarUntar(t *testing.T) {
|
||||
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)
|
||||
if file.fileType == RegularFile {
|
||||
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()
|
||||
}
|
||||
} else if file.fileType == SymLink {
|
||||
err := os.Symlink(file.data, filepath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
t.Errorf("unexpected file type: %v", file)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
writer := &bytes.Buffer{}
|
||||
if err := makeTar(dir, dir2, writer); err != nil {
|
||||
if err := makeTar(dir, dir, writer); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@ -170,16 +199,35 @@ func TestTarUntar(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
filepath := path.Join(dir, file.name)
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
buff := &bytes.Buffer{}
|
||||
io.Copy(buff, f)
|
||||
if file.data != string(buff.Bytes()) {
|
||||
t.Errorf("expected: %s, saw: %s", file.data, string(buff.Bytes()))
|
||||
absPath := dir2 + strings.TrimPrefix(dir, os.TempDir())
|
||||
filepath := path.Join(absPath, file.name)
|
||||
|
||||
if file.fileType == RegularFile {
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
buff := &bytes.Buffer{}
|
||||
io.Copy(buff, f)
|
||||
|
||||
if file.data != string(buff.Bytes()) {
|
||||
t.Errorf("expected: %s, saw: %s", file.data, string(buff.Bytes()))
|
||||
}
|
||||
} else if file.fileType == SymLink {
|
||||
dest, err := os.Readlink(filepath)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if file.data != dest {
|
||||
t.Errorf("expected: %s, saw: %s", file.data, dest)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("unexpected file type: %v", file)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user