mirror of https://github.com/k3s-io/k3s
Merge pull request #69573 from bjhaid/master
Opt out of chowning and chmoding from kubectl cp.pull/58/head
commit
00dd32b167
|
@ -23,6 +23,7 @@ go_test(
|
||||||
srcs = ["cp_test.go"],
|
srcs = ["cp_test.go"],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/kubectl/cmd/exec:go_default_library",
|
||||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||||
"//pkg/kubectl/scheme:go_default_library",
|
"//pkg/kubectl/scheme:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
|
|
@ -69,6 +69,7 @@ var (
|
||||||
type CopyOptions struct {
|
type CopyOptions struct {
|
||||||
Container string
|
Container string
|
||||||
Namespace string
|
Namespace string
|
||||||
|
NoPreserve bool
|
||||||
|
|
||||||
ClientConfig *restclient.Config
|
ClientConfig *restclient.Config
|
||||||
Clientset kubernetes.Interface
|
Clientset kubernetes.Interface
|
||||||
|
@ -98,6 +99,7 @@ func NewCmdCp(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.C
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().StringVarP(&o.Container, "container", "c", o.Container, "Container name. If omitted, the first container in the pod will be chosen")
|
cmd.Flags().StringVarP(&o.Container, "container", "c", o.Container, "Container name. If omitted, the first container in the pod will be chosen")
|
||||||
|
cmd.Flags().BoolVarP(&o.NoPreserve, "no-preserve", "", false, "The copied file/directory's ownership and permissions will not be preserved in the container")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -179,7 +181,7 @@ func (o *CopyOptions) Run(args []string) error {
|
||||||
|
|
||||||
if len(srcSpec.PodName) != 0 && len(destSpec.PodName) != 0 {
|
if len(srcSpec.PodName) != 0 && len(destSpec.PodName) != 0 {
|
||||||
if _, err := os.Stat(args[0]); err == nil {
|
if _, err := os.Stat(args[0]); err == nil {
|
||||||
return o.copyToPod(fileSpec{File: args[0]}, destSpec)
|
return o.copyToPod(fileSpec{File: args[0]}, destSpec, &exec.ExecOptions{})
|
||||||
}
|
}
|
||||||
return fmt.Errorf("src doesn't exist in local filesystem")
|
return fmt.Errorf("src doesn't exist in local filesystem")
|
||||||
}
|
}
|
||||||
|
@ -188,7 +190,7 @@ func (o *CopyOptions) Run(args []string) error {
|
||||||
return o.copyFromPod(srcSpec, destSpec)
|
return o.copyFromPod(srcSpec, destSpec)
|
||||||
}
|
}
|
||||||
if len(destSpec.PodName) != 0 {
|
if len(destSpec.PodName) != 0 {
|
||||||
return o.copyToPod(srcSpec, destSpec)
|
return o.copyToPod(srcSpec, destSpec, &exec.ExecOptions{})
|
||||||
}
|
}
|
||||||
return fmt.Errorf("one of src or dest must be a remote file specification")
|
return fmt.Errorf("one of src or dest must be a remote file specification")
|
||||||
}
|
}
|
||||||
|
@ -216,7 +218,7 @@ func (o *CopyOptions) checkDestinationIsDir(dest fileSpec) error {
|
||||||
return o.execute(options)
|
return o.execute(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *CopyOptions) copyToPod(src, dest fileSpec) error {
|
func (o *CopyOptions) copyToPod(src, dest fileSpec, options *exec.ExecOptions) error {
|
||||||
if len(src.File) == 0 || len(dest.File) == 0 {
|
if len(src.File) == 0 || len(dest.File) == 0 {
|
||||||
return errFileCannotBeEmpty
|
return errFileCannotBeEmpty
|
||||||
}
|
}
|
||||||
|
@ -238,16 +240,20 @@ func (o *CopyOptions) copyToPod(src, dest fileSpec) error {
|
||||||
err := makeTar(src.File, dest.File, writer)
|
err := makeTar(src.File, dest.File, writer)
|
||||||
cmdutil.CheckErr(err)
|
cmdutil.CheckErr(err)
|
||||||
}()
|
}()
|
||||||
|
var cmdArr []string
|
||||||
|
|
||||||
// TODO: Improve error messages by first testing if 'tar' is present in the container?
|
// TODO: Improve error messages by first testing if 'tar' is present in the container?
|
||||||
cmdArr := []string{"tar", "xf", "-"}
|
if o.NoPreserve {
|
||||||
|
cmdArr = []string{"tar", "--no-same-permissions", "--no-same-owner", "-xf", "-"}
|
||||||
|
} else {
|
||||||
|
cmdArr = []string{"tar", "-xf", "-"}
|
||||||
|
}
|
||||||
destDir := path.Dir(dest.File)
|
destDir := path.Dir(dest.File)
|
||||||
if len(destDir) > 0 {
|
if len(destDir) > 0 {
|
||||||
cmdArr = append(cmdArr, "-C", destDir)
|
cmdArr = append(cmdArr, "-C", destDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
options := &exec.ExecOptions{
|
options.StreamOptions = exec.StreamOptions{
|
||||||
StreamOptions: exec.StreamOptions{
|
|
||||||
IOStreams: genericclioptions.IOStreams{
|
IOStreams: genericclioptions.IOStreams{
|
||||||
In: reader,
|
In: reader,
|
||||||
Out: o.Out,
|
Out: o.Out,
|
||||||
|
@ -257,11 +263,10 @@ func (o *CopyOptions) copyToPod(src, dest fileSpec) error {
|
||||||
|
|
||||||
Namespace: dest.PodNamespace,
|
Namespace: dest.PodNamespace,
|
||||||
PodName: dest.PodName,
|
PodName: dest.PodName,
|
||||||
},
|
|
||||||
|
|
||||||
Command: cmdArr,
|
|
||||||
Executor: &exec.DefaultRemoteExecutor{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.Command = cmdArr
|
||||||
|
options.Executor = &exec.DefaultRemoteExecutor{}
|
||||||
return o.execute(options)
|
return o.execute(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
|
kexec "k8s.io/kubernetes/pkg/kubectl/cmd/exec"
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||||
)
|
)
|
||||||
|
@ -629,7 +631,7 @@ func TestCopyToPod(t *testing.T) {
|
||||||
}
|
}
|
||||||
opts.Complete(tf, cmd)
|
opts.Complete(tf, cmd)
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err = opts.copyToPod(src, dest)
|
err = opts.copyToPod(src, dest, &kexec.ExecOptions{})
|
||||||
//If error is NotFound error , it indicates that the
|
//If error is NotFound error , it indicates that the
|
||||||
//request has been sent correctly.
|
//request has been sent correctly.
|
||||||
//Treat this as no error.
|
//Treat this as no error.
|
||||||
|
@ -643,6 +645,68 @@ func TestCopyToPod(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCopyToPodNoPreserve(t *testing.T) {
|
||||||
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||||
|
ns := scheme.Codecs
|
||||||
|
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||||
|
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
responsePod := &v1.Pod{}
|
||||||
|
return &http.Response{StatusCode: http.StatusNotFound, Header: cmdtesting.DefaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, responsePod))))}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
|
||||||
|
ioStreams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
|
||||||
|
cmd := NewCmdCp(tf, ioStreams)
|
||||||
|
|
||||||
|
srcFile, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(srcFile)
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
expectedCmd []string
|
||||||
|
nopreserve bool
|
||||||
|
}{
|
||||||
|
"copy to pod no preserve user and permissions": {
|
||||||
|
expectedCmd: []string{"tar", "--no-same-permissions", "--no-same-owner", "-xf", "-", "-C", "."},
|
||||||
|
nopreserve: true,
|
||||||
|
},
|
||||||
|
"copy to pod preserve user and permissions": {
|
||||||
|
expectedCmd: []string{"tar", "-xf", "-", "-C", "."},
|
||||||
|
nopreserve: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
opts := NewCopyOptions(ioStreams)
|
||||||
|
src := fileSpec{
|
||||||
|
File: srcFile,
|
||||||
|
}
|
||||||
|
dest := fileSpec{
|
||||||
|
PodNamespace: "pod-ns",
|
||||||
|
PodName: "pod-name",
|
||||||
|
File: "foo",
|
||||||
|
}
|
||||||
|
opts.Complete(tf, cmd)
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
options := &kexec.ExecOptions{}
|
||||||
|
opts.NoPreserve = test.nopreserve
|
||||||
|
err = opts.copyToPod(src, dest, options)
|
||||||
|
if !(reflect.DeepEqual(test.expectedCmd, options.Command)) {
|
||||||
|
t.Errorf("expected cmd: %v, got: %v", test.expectedCmd, options.Command)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidate(t *testing.T) {
|
func TestValidate(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
Loading…
Reference in New Issue