diff --git a/pkg/controller/volume/attachdetach/BUILD b/pkg/controller/volume/attachdetach/BUILD index 47aff56a075..f4254b70d2d 100644 --- a/pkg/controller/volume/attachdetach/BUILD +++ b/pkg/controller/volume/attachdetach/BUILD @@ -46,6 +46,7 @@ go_library( "//staging/src/k8s.io/client-go/util/workqueue:go_default_library", "//staging/src/k8s.io/cloud-provider:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", ], ) diff --git a/pkg/controller/volume/attachdetach/attach_detach_controller.go b/pkg/controller/volume/attachdetach/attach_detach_controller.go index 75fe4bc2ed5..80d60cb5e27 100644 --- a/pkg/controller/volume/attachdetach/attach_detach_controller.go +++ b/pkg/controller/volume/attachdetach/attach_detach_controller.go @@ -45,6 +45,8 @@ import ( "k8s.io/client-go/util/workqueue" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog" + utilexec "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache" "k8s.io/kubernetes/pkg/controller/volume/attachdetach/metrics" "k8s.io/kubernetes/pkg/controller/volume/attachdetach/populator" @@ -768,8 +770,8 @@ func (adc *attachDetachController) DeleteServiceAccountTokenFunc() func(types.UI } } -func (adc *attachDetachController) GetExec(pluginName string) mount.Exec { - return mount.NewOSExec() +func (adc *attachDetachController) GetExec(pluginName string) utilexec.Interface { + return utilexec.New() } func (adc *attachDetachController) addNodeToDswp(node *v1.Node, nodeName types.NodeName) { diff --git a/pkg/controller/volume/expand/BUILD b/pkg/controller/volume/expand/BUILD index d814ed0108e..d04ccfeae5b 100644 --- a/pkg/controller/volume/expand/BUILD +++ b/pkg/controller/volume/expand/BUILD @@ -33,6 +33,7 @@ go_library( "//staging/src/k8s.io/client-go/util/workqueue:go_default_library", "//staging/src/k8s.io/cloud-provider:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", ], ) diff --git a/pkg/controller/volume/expand/expand_controller.go b/pkg/controller/volume/expand/expand_controller.go index c21f55fc0b9..60319e1ef52 100644 --- a/pkg/controller/volume/expand/expand_controller.go +++ b/pkg/controller/volume/expand/expand_controller.go @@ -41,6 +41,8 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" cloudprovider "k8s.io/cloud-provider" + utilexec "k8s.io/utils/exec" + v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" "k8s.io/kubernetes/pkg/controller/volume/events" "k8s.io/kubernetes/pkg/util/mount" @@ -380,8 +382,8 @@ func (expc *expandController) GetMounter(pluginName string) mount.Interface { return nil } -func (expc *expandController) GetExec(pluginName string) mount.Exec { - return mount.NewOSExec() +func (expc *expandController) GetExec(pluginName string) utilexec.Interface { + return utilexec.New() } func (expc *expandController) GetHostName() string { diff --git a/pkg/controller/volume/persistentvolume/BUILD b/pkg/controller/volume/persistentvolume/BUILD index 310af33dfed..7aa1337a541 100644 --- a/pkg/controller/volume/persistentvolume/BUILD +++ b/pkg/controller/volume/persistentvolume/BUILD @@ -56,6 +56,7 @@ go_library( "//staging/src/k8s.io/cloud-provider/volume/errors:go_default_library", "//staging/src/k8s.io/csi-translation-lib:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", ], ) diff --git a/pkg/controller/volume/persistentvolume/volume_host.go b/pkg/controller/volume/persistentvolume/volume_host.go index 371807e4c6c..d35e9a963e3 100644 --- a/pkg/controller/volume/persistentvolume/volume_host.go +++ b/pkg/controller/volume/persistentvolume/volume_host.go @@ -27,6 +27,8 @@ import ( "k8s.io/client-go/tools/record" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog" + utilexec "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/util/mount" vol "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util/subpath" @@ -116,8 +118,8 @@ func (ctrl *PersistentVolumeController) DeleteServiceAccountTokenFunc() func(typ } } -func (adc *PersistentVolumeController) GetExec(pluginName string) mount.Exec { - return mount.NewOSExec() +func (adc *PersistentVolumeController) GetExec(pluginName string) utilexec.Interface { + return utilexec.New() } func (ctrl *PersistentVolumeController) GetNodeLabels() (map[string]string, error) { diff --git a/pkg/kubelet/volume_host.go b/pkg/kubelet/volume_host.go index 964b11a5c78..da5c2563d38 100644 --- a/pkg/kubelet/volume_host.go +++ b/pkg/kubelet/volume_host.go @@ -34,6 +34,8 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" cloudprovider "k8s.io/cloud-provider" + utilexec "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/configmap" "k8s.io/kubernetes/pkg/kubelet/secret" @@ -86,7 +88,7 @@ func NewInitializedVolumePluginMgr( informerFactory: informerFactory, csiDriverLister: csiDriverLister, csiDriversSynced: csiDriversSynced, - exec: mount.NewOSExec(), + exec: utilexec.New(), } if err := kvh.volumePluginMgr.InitPlugins(plugins, prober, kvh); err != nil { @@ -115,7 +117,7 @@ type kubeletVolumeHost struct { informerFactory informers.SharedInformerFactory csiDriverLister storagelisters.CSIDriverLister csiDriversSynced cache.InformerSynced - exec mount.Exec + exec utilexec.Interface } func (kvh *kubeletVolumeHost) SetKubeletError(err error) { @@ -271,6 +273,6 @@ func (kvh *kubeletVolumeHost) GetEventRecorder() record.EventRecorder { return kvh.kubelet.recorder } -func (kvh *kubeletVolumeHost) GetExec(pluginName string) mount.Exec { +func (kvh *kubeletVolumeHost) GetExec(pluginName string) utilexec.Interface { return kvh.exec } diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index 6187e72c222..a24ce6d68e0 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -4,7 +4,6 @@ go_library( name = "go_default_library", srcs = [ "doc.go", - "exec.go", "fake_exec.go", "fake_mounter.go", "mount.go", @@ -75,6 +74,7 @@ go_test( ], embed = [":go_default_library"], deps = [ + "//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec/testing:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:windows": [ diff --git a/pkg/util/mount/exec.go b/pkg/util/mount/exec.go deleted file mode 100644 index be3074af47b..00000000000 --- a/pkg/util/mount/exec.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package mount - -import "k8s.io/utils/exec" - -// NewOSExec returns a new Exec interface implementation based on exec() -func NewOSExec() *OSExec { - return &OSExec{} -} - -// OSExec is an implementation of Exec interface that uses simple utils.Exec -type OSExec struct{} - -var _ Exec = &OSExec{} - -// Run exucutes the given cmd and arges and returns stdout and stderr as a -// combined byte stream -func (e *OSExec) Run(cmd string, args ...string) ([]byte, error) { - exe := exec.New() - return exe.Command(cmd, args...).CombinedOutput() -} diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index 7d94a48f4b0..b0be60cdf65 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -23,6 +23,8 @@ import ( "os" "path/filepath" "strings" + + utilexec "k8s.io/utils/exec" ) const ( @@ -53,13 +55,6 @@ type Interface interface { GetMountRefs(pathname string) ([]string, error) } -// Exec is an interface for executing commands on systems. -type Exec interface { - // Run executes a command and returns its stdout + stderr combined in one - // stream. - Run(cmd string, args ...string) ([]byte, error) -} - // Compile-time check to ensure all Mounter implementations satisfy // the mount interface. var _ Interface = &Mounter{} @@ -79,7 +74,7 @@ type MountPoint struct { // mounts it otherwise the device is formatted first then mounted. type SafeFormatAndMount struct { Interface - Exec + Exec utilexec.Interface } // FormatAndMount formats the given disk, if needed, and mounts it. diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index c0c4b0dd823..2a910024297 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -273,7 +273,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, // Run fsck on the disk to fix repairable issues, only do this for volumes requested as rw. klog.V(4).Infof("Checking for issues with fsck on disk: %s", source) args := []string{"-a", source} - out, err := mounter.Exec.Run("fsck", args...) + out, err := mounter.Exec.Command("fsck", args...).CombinedOutput() if err != nil { ee, isExitError := err.(utilexec.ExitError) switch { @@ -320,7 +320,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, } } klog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args) - _, err := mounter.Exec.Run("mkfs."+fstype, args...) + _, err := mounter.Exec.Command("mkfs."+fstype, args...).CombinedOutput() if err == nil { // the disk has been formatted successfully try to mount it again. klog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target) @@ -344,7 +344,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) { args := []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", disk} klog.V(4).Infof("Attempting to determine if disk %q is formatted using blkid with args: (%v)", disk, args) - dataOut, err := mounter.Exec.Run("blkid", args...) + dataOut, err := mounter.Exec.Command("blkid", args...).CombinedOutput() output := string(dataOut) klog.V(4).Infof("Output: %q, err: %v", output, err) diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index f77931d1446..9371ff43551 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -26,6 +26,7 @@ import ( "strings" "k8s.io/klog" + utilexec "k8s.io/utils/exec" "k8s.io/utils/keymutex" utilpath "k8s.io/utils/path" ) @@ -219,7 +220,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, // format disk if it is unformatted(raw) cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+ " | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype) - if output, err := mounter.Exec.Run("powershell", "/c", cmd); err != nil { + if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil { return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output)) } klog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype) @@ -231,7 +232,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, driverPath := driveLetter + ":" target = NormalizeWindowsPath(target) klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target) - if output, err := mounter.Exec.Run("cmd", "/c", "mklink", "/D", target, driverPath); err != nil { + if output, err := mounter.Exec.Command("cmd", "/c", "mklink", "/D", target, driverPath).CombinedOutput(); err != nil { klog.Errorf("mklink failed: %v, output: %q", err, string(output)) return err } @@ -239,9 +240,9 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, } // Get drive letter according to windows disk number -func getDriveLetterByDiskNumber(diskNum string, exec Exec) (string, error) { +func getDriveLetterByDiskNumber(diskNum string, exec utilexec.Interface) (string, error) { cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum) - output, err := exec.Run("powershell", "/c", cmd) + output, err := exec.Command("powershell", "/c", cmd).CombinedOutput() if err != nil { return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output)) } diff --git a/pkg/util/mount/mount_windows_test.go b/pkg/util/mount/mount_windows_test.go index bfcbc7534dd..288cc633518 100644 --- a/pkg/util/mount/mount_windows_test.go +++ b/pkg/util/mount/mount_windows_test.go @@ -24,10 +24,10 @@ import ( "os" "os/exec" "path/filepath" - "strings" "testing" "github.com/stretchr/testify/assert" + "k8s.io/utils/exec/testing" ) func makeLink(link, target string) error { @@ -224,61 +224,61 @@ func TestIsLikelyNotMountPoint(t *testing.T) { } func TestFormatAndMount(t *testing.T) { - fakeMounter := ErrorMounter{NewFakeMounter(nil), 0, nil} - execCallback := func(cmd string, args ...string) ([]byte, error) { - for j := range args { - if strings.Contains(args[j], "Get-Disk -Number") { - return []byte("0"), nil - } - - if strings.Contains(args[j], "Get-Partition -DiskNumber") { - return []byte("0"), nil - } - - if strings.Contains(args[j], "mklink") { - return nil, nil - } - } - return nil, fmt.Errorf("Unexpected cmd %s, args %v", cmd, args) - } - fakeExec := NewFakeExec(execCallback) - - mounter := SafeFormatAndMount{ - Interface: &fakeMounter, - Exec: fakeExec, - } - tests := []struct { device string target string fstype string + execScripts []ExecArgs mountOptions []string expectError bool }{ { - "0", - "disk", - "NTFS", - []string{}, - false, + device: "0", + target: "disk", + fstype: "NTFS", + execScripts: []ExecArgs{ + {"powershell", []string{"/c", "Get-Disk", "-Number"}, "0", nil}, + {"powershell", []string{"/c", "Get-Partition", "-DiskNumber"}, "0", nil}, + {"cmd", []string{"/c", "mklink", "/D"}, "", nil}, + }, + mountOptions: []string{}, + expectError: false, }, { - "0", - "disk", - "", - []string{}, - false, + device: "0", + target: "disk", + fstype: "", + execScripts: []ExecArgs{ + {"powershell", []string{"/c", "Get-Disk", "-Number"}, "0", nil}, + {"powershell", []string{"/c", "Get-Partition", "-DiskNumber"}, "0", nil}, + {"cmd", []string{"/c", "mklink", "/D"}, "", nil}, + }, + mountOptions: []string{}, + expectError: false, }, { - "invalidDevice", - "disk", - "NTFS", - []string{}, - true, + device: "invalidDevice", + target: "disk", + fstype: "NTFS", + mountOptions: []string{}, + expectError: true, }, } for _, test := range tests { + fakeMounter := ErrorMounter{NewFakeMounter(nil), 0, nil} + fakeExec := &testingexec.FakeExec{} + for _, script := range test.execScripts { + fakeCmd := &testingexec.FakeCmd{} + cmdAction := makeFakeCmd(fakeCmd, script.command, script.args...) + outputAction := makeFakeOutput(script.output, script.err) + fakeCmd.CombinedOutputScript = append(fakeCmd.CombinedOutputScript, outputAction) + fakeExec.CommandScript = append(fakeExec.CommandScript, cmdAction) + } + mounter := SafeFormatAndMount{ + Interface: &fakeMounter, + Exec: fakeExec, + } base, err := ioutil.TempDir("", test.device) if err != nil { t.Fatalf(err.Error()) diff --git a/pkg/util/mount/safe_format_and_mount_test.go b/pkg/util/mount/safe_format_and_mount_test.go index 4a7241ce512..d6cc06bce58 100644 --- a/pkg/util/mount/safe_format_and_mount_test.go +++ b/pkg/util/mount/safe_format_and_mount_test.go @@ -24,7 +24,8 @@ import ( "strings" "testing" - fakeexec "k8s.io/utils/exec/testing" + "k8s.io/utils/exec" + "k8s.io/utils/exec/testing" ) type ErrorMounter struct { @@ -82,7 +83,7 @@ func TestSafeFormatAndMount(t *testing.T) { description: "Test 'fsck' fails with exit status 4", fstype: "ext4", execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}}, + {"fsck", []string{"-a", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 4}}, }, expectedError: fmt.Errorf("'fsck' found errors on device /dev/foo but could not correct them"), }, @@ -90,14 +91,14 @@ func TestSafeFormatAndMount(t *testing.T) { description: "Test 'fsck' fails with exit status 1 (errors found and corrected)", fstype: "ext4", execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 1}}, + {"fsck", []string{"-a", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 1}}, }, }, { description: "Test 'fsck' fails with exit status other than 1 and 4 (likely unformatted device)", fstype: "ext4", execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 8}}, + {"fsck", []string{"-a", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 8}}, }, }, { @@ -116,7 +117,7 @@ func TestSafeFormatAndMount(t *testing.T) { mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")}, execScripts: []ExecArgs{ {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}}, + {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}}, {"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", fmt.Errorf("formatting failed")}, }, expectedError: fmt.Errorf("formatting failed"), @@ -127,7 +128,7 @@ func TestSafeFormatAndMount(t *testing.T) { mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")}, execScripts: []ExecArgs{ {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}}, + {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}}, {"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", nil}, }, expectedError: fmt.Errorf("Still cannot mount"), @@ -138,7 +139,7 @@ func TestSafeFormatAndMount(t *testing.T) { mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, execScripts: []ExecArgs{ {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}}, + {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}}, {"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", nil}, }, expectedError: nil, @@ -149,7 +150,7 @@ func TestSafeFormatAndMount(t *testing.T) { mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, execScripts: []ExecArgs{ {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}}, + {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}}, {"mkfs.ext3", []string{"-F", "-m0", "/dev/foo"}, "", nil}, }, expectedError: nil, @@ -160,7 +161,7 @@ func TestSafeFormatAndMount(t *testing.T) { mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, execScripts: []ExecArgs{ {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}}, + {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}}, {"mkfs.xfs", []string{"/dev/foo"}, "", nil}, }, expectedError: nil, @@ -181,7 +182,7 @@ func TestSafeFormatAndMount(t *testing.T) { mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, execScripts: []ExecArgs{ {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}}, + {"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 4}}, {"mkfs.xfs", []string{"/dev/foo"}, "", nil}, }, expectedError: fmt.Errorf("exit 4"), @@ -189,27 +190,15 @@ func TestSafeFormatAndMount(t *testing.T) { } for _, test := range tests { - execCallCount := 0 - execCallback := func(cmd string, args ...string) ([]byte, error) { - if len(test.execScripts) <= execCallCount { - t.Errorf("Unexpected command: %s %v", cmd, args) - return nil, nil - } - script := test.execScripts[execCallCount] - execCallCount++ - if script.command != cmd { - t.Errorf("Unexpected command %s. Expecting %s", cmd, script.command) - } - for j := range args { - if args[j] != script.args[j] { - t.Errorf("Unexpected args %v. Expecting %v", args, script.args) - } - } - return []byte(script.output), script.err - } - fakeMounter := ErrorMounter{NewFakeMounter(nil), 0, test.mountErrs} - fakeExec := NewFakeExec(execCallback) + fakeExec := &testingexec.FakeExec{ExactOrder: true} + for _, script := range test.execScripts { + fakeCmd := &testingexec.FakeCmd{} + cmdAction := makeFakeCmd(fakeCmd, script.command, script.args...) + outputAction := makeFakeOutput(script.output, script.err) + fakeCmd.CombinedOutputScript = append(fakeCmd.CombinedOutputScript, outputAction) + fakeExec.CommandScript = append(fakeExec.CommandScript, cmdAction) + } mounter := SafeFormatAndMount{ Interface: &fakeMounter, Exec: fakeExec, @@ -241,3 +230,19 @@ func TestSafeFormatAndMount(t *testing.T) { } } } + +func makeFakeCmd(fakeCmd *testingexec.FakeCmd, cmd string, args ...string) testingexec.FakeCommandAction { + c := cmd + a := args + return func(cmd string, args ...string) exec.Cmd { + command := testingexec.InitFakeCmd(fakeCmd, c, a...) + return command + } +} + +func makeFakeOutput(output string, err error) testingexec.FakeCombinedOutputAction { + o := output + return func() ([]byte, error) { + return []byte(o), err + } +} diff --git a/pkg/util/resizefs/resizefs_linux.go b/pkg/util/resizefs/resizefs_linux.go index 4eabdb1ddc0..1d2d5e0fca9 100644 --- a/pkg/util/resizefs/resizefs_linux.go +++ b/pkg/util/resizefs/resizefs_linux.go @@ -61,7 +61,7 @@ func (resizefs *ResizeFs) Resize(devicePath string, deviceMountPath string) (boo } func (resizefs *ResizeFs) extResize(devicePath string) (bool, error) { - output, err := resizefs.mounter.Exec.Run("resize2fs", devicePath) + output, err := resizefs.mounter.Exec.Command("resize2fs", devicePath).CombinedOutput() if err == nil { klog.V(2).Infof("Device %s resized successfully", devicePath) return true, nil @@ -74,7 +74,7 @@ func (resizefs *ResizeFs) extResize(devicePath string) (bool, error) { func (resizefs *ResizeFs) xfsResize(deviceMountPath string) (bool, error) { args := []string{"-d", deviceMountPath} - output, err := resizefs.mounter.Exec.Run("xfs_growfs", args...) + output, err := resizefs.mounter.Exec.Command("xfs_growfs", args...).CombinedOutput() if err == nil { klog.V(2).Infof("Device %s resized successfully", deviceMountPath) diff --git a/pkg/volume/BUILD b/pkg/volume/BUILD index fd864758f71..46956c0de1e 100644 --- a/pkg/volume/BUILD +++ b/pkg/volume/BUILD @@ -40,6 +40,7 @@ go_library( "//staging/src/k8s.io/client-go/tools/record:go_default_library", "//staging/src/k8s.io/cloud-provider:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", ], ) diff --git a/pkg/volume/awsebs/attacher_windows.go b/pkg/volume/awsebs/attacher_windows.go index 6ad160410c2..e60d2999410 100644 --- a/pkg/volume/awsebs/attacher_windows.go +++ b/pkg/volume/awsebs/attacher_windows.go @@ -41,7 +41,7 @@ func (attacher *awsElasticBlockStoreAttacher) getDiskNumber(volumeID string) (st volumeID = split[len(split)-1] exec := attacher.host.GetExec(awsElasticBlockStorePluginName) - output, err := exec.Run(ebsnvmeID) + output, err := exec.Command(ebsnvmeID).CombinedOutput() if err != nil { return "", fmt.Errorf("error calling ebsnvme-id.exe: %v", err) } diff --git a/pkg/volume/azure_dd/BUILD b/pkg/volume/azure_dd/BUILD index e9345051380..ed072b52ef5 100644 --- a/pkg/volume/azure_dd/BUILD +++ b/pkg/volume/azure_dd/BUILD @@ -43,7 +43,45 @@ go_library( "//vendor/github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage:go_default_library", "//vendor/k8s.io/klog:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", - ], + ] + select({ + "@io_bazel_rules_go//go/platform:android": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:ios": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:nacl": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "//conditions:default": [], + }), ) filegroup( @@ -70,7 +108,6 @@ go_test( ], embed = [":go_default_library"], deps = [ - "//pkg/util/mount:go_default_library", "//pkg/volume:go_default_library", "//pkg/volume/testing:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", @@ -79,5 +116,6 @@ go_test( "//staging/src/k8s.io/client-go/util/testing:go_default_library", "//vendor/github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", ], ) diff --git a/pkg/volume/azure_dd/azure_common_linux.go b/pkg/volume/azure_dd/azure_common_linux.go index 89a08750ca6..f37e8686a15 100644 --- a/pkg/volume/azure_dd/azure_common_linux.go +++ b/pkg/volume/azure_dd/azure_common_linux.go @@ -26,7 +26,7 @@ import ( libstrings "strings" "k8s.io/klog" - "k8s.io/kubernetes/pkg/util/mount" + utilexec "k8s.io/utils/exec" ) // exclude those used by azure as resource and OS root in /dev/disk/azure @@ -69,7 +69,7 @@ func getDiskLinkByDevName(io ioHandler, devLinkPath, devName string) (string, er return "", fmt.Errorf("read %s error: %v", devLinkPath, err) } -func scsiHostRescan(io ioHandler, exec mount.Exec) { +func scsiHostRescan(io ioHandler, exec utilexec.Interface) { scsi_path := "/sys/class/scsi_host/" if dirs, err := io.ReadDir(scsi_path); err == nil { for _, f := range dirs { @@ -84,7 +84,7 @@ func scsiHostRescan(io ioHandler, exec mount.Exec) { } } -func findDiskByLun(lun int, io ioHandler, exec mount.Exec) (string, error) { +func findDiskByLun(lun int, io ioHandler, exec utilexec.Interface) (string, error) { azureDisks := listAzureDiskPath(io) return findDiskByLunWithConstraint(lun, io, azureDisks) } diff --git a/pkg/volume/azure_dd/azure_common_test.go b/pkg/volume/azure_dd/azure_common_test.go index 0089bb87654..16223e00a30 100644 --- a/pkg/volume/azure_dd/azure_common_test.go +++ b/pkg/volume/azure_dd/azure_common_test.go @@ -28,8 +28,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "github.com/stretchr/testify/assert" - - "k8s.io/kubernetes/pkg/util/mount" + "k8s.io/utils/exec" ) type fakeFileInfo struct { @@ -126,7 +125,7 @@ func TestIoHandler(t *testing.T) { if runtime.GOOS != "windows" && runtime.GOOS != "linux" { t.Skipf("TestIoHandler not supported on GOOS=%s", runtime.GOOS) } - disk, err := findDiskByLun(lun, &fakeIOHandler{}, mount.NewOSExec()) + disk, err := findDiskByLun(lun, &fakeIOHandler{}, exec.New()) if runtime.GOOS == "windows" { if err != nil { t.Errorf("no data disk found: disk %v err %v", disk, err) diff --git a/pkg/volume/azure_dd/azure_common_unsupported.go b/pkg/volume/azure_dd/azure_common_unsupported.go index 842f023332f..550506d6df5 100644 --- a/pkg/volume/azure_dd/azure_common_unsupported.go +++ b/pkg/volume/azure_dd/azure_common_unsupported.go @@ -19,11 +19,11 @@ limitations under the License. package azure_dd -import "k8s.io/kubernetes/pkg/util/mount" +import "k8s.io/utils/exec" -func scsiHostRescan(io ioHandler, exec mount.Exec) { +func scsiHostRescan(io ioHandler, exec exec.Interface) { } -func findDiskByLun(lun int, io ioHandler, exec mount.Exec) (string, error) { +func findDiskByLun(lun int, io ioHandler, exec exec.Interface) (string, error) { return "", nil } diff --git a/pkg/volume/azure_dd/azure_common_windows.go b/pkg/volume/azure_dd/azure_common_windows.go index 85bff9ccacd..502e8c3cc9b 100644 --- a/pkg/volume/azure_dd/azure_common_windows.go +++ b/pkg/volume/azure_dd/azure_common_windows.go @@ -26,22 +26,23 @@ import ( "strings" "k8s.io/klog" + utilexec "k8s.io/utils/exec" "k8s.io/kubernetes/pkg/util/mount" ) -func scsiHostRescan(io ioHandler, exec mount.Exec) { +func scsiHostRescan(io ioHandler, exec utilexec.Interface) { cmd := "Update-HostStorageCache" - output, err := exec.Run("powershell", "/c", cmd) + output, err := exec.Command("powershell", "/c", cmd).CombinedOutput() if err != nil { klog.Errorf("Update-HostStorageCache failed in scsiHostRescan, error: %v, output: %q", err, string(output)) } } // search Windows disk number by LUN -func findDiskByLun(lun int, iohandler ioHandler, exec mount.Exec) (string, error) { +func findDiskByLun(lun int, iohandler ioHandler, exec utilexec.Interface) (string, error) { cmd := `Get-Disk | select number, location | ConvertTo-Json` - output, err := exec.Run("powershell", "/c", cmd) + output, err := exec.Command("powershell", "/c", cmd).CombinedOutput() if err != nil { klog.Errorf("Get-Disk failed in findDiskByLun, error: %v, output: %q", err, string(output)) return "", err @@ -98,7 +99,7 @@ func findDiskByLun(lun int, iohandler ioHandler, exec mount.Exec) (string, error return "", nil } -func formatIfNotFormatted(disk string, fstype string, exec mount.Exec) { +func formatIfNotFormatted(disk string, fstype string, exec utilexec.Interface) { if err := mount.ValidateDiskNumber(disk); err != nil { klog.Errorf("azureDisk Mount: formatIfNotFormatted failed, err: %v\n", err) return @@ -110,7 +111,7 @@ func formatIfNotFormatted(disk string, fstype string, exec mount.Exec) { } cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru", disk) cmd += fmt.Sprintf(" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", fstype) - output, err := exec.Run("powershell", "/c", cmd) + output, err := exec.Command("powershell", "/c", cmd).CombinedOutput() if err != nil { klog.Errorf("azureDisk Mount: Get-Disk failed, error: %v, output: %q", err, string(output)) } else { diff --git a/pkg/volume/cephfs/cephfs.go b/pkg/volume/cephfs/cephfs.go index 590e7e4b7ca..f8a6280c7c0 100644 --- a/pkg/volume/cephfs/cephfs.go +++ b/pkg/volume/cephfs/cephfs.go @@ -331,7 +331,7 @@ func (cephfsVolume *cephfsMounter) checkFuseMount() bool { execute := cephfsVolume.plugin.host.GetExec(cephfsVolume.plugin.GetPluginName()) switch runtime.GOOS { case "linux": - if _, err := execute.Run("/usr/bin/test", "-x", "/sbin/mount.fuse.ceph"); err == nil { + if _, err := execute.Command("/usr/bin/test", "-x", "/sbin/mount.fuse.ceph").CombinedOutput(); err == nil { klog.V(4).Info("/sbin/mount.fuse.ceph exists, it should be fuse mount.") return true } diff --git a/pkg/volume/fc/BUILD b/pkg/volume/fc/BUILD index bec107d7dc7..ccc2d64e772 100644 --- a/pkg/volume/fc/BUILD +++ b/pkg/volume/fc/BUILD @@ -27,6 +27,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", ], ) @@ -48,6 +49,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/util/testing:go_default_library", + "//vendor/k8s.io/utils/exec/testing:go_default_library", ], ) diff --git a/pkg/volume/fc/fc.go b/pkg/volume/fc/fc.go index ee8a09226e4..4892f4b4a4d 100644 --- a/pkg/volume/fc/fc.go +++ b/pkg/volume/fc/fc.go @@ -28,6 +28,8 @@ import ( "k8s.io/apimachinery/pkg/types" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/klog" + utilexec "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" @@ -114,7 +116,7 @@ func (plugin *fcPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.Volu return plugin.newMounterInternal(spec, pod.UID, &fcUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *fcPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec) (volume.Mounter, error) { +func (plugin *fcPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.Mounter, error) { // fc volumes used directly in a pod have a ReadOnly flag set by the pod author. // fc volumes used as a PersistentVolume gets the ReadOnly flag indirectly through the persistent-claim volume used to mount the PV fc, readOnly, err := getVolumeSource(spec) @@ -174,7 +176,7 @@ func (plugin *fcPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ v return plugin.newBlockVolumeMapperInternal(spec, uid, &fcUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *fcPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec) (volume.BlockVolumeMapper, error) { +func (plugin *fcPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.BlockVolumeMapper, error) { fc, readOnly, err := getVolumeSource(spec) if err != nil { return nil, err diff --git a/pkg/volume/fc/fc_test.go b/pkg/volume/fc/fc_test.go index 4ea372a22ff..b0d3bfe3265 100644 --- a/pkg/volume/fc/fc_test.go +++ b/pkg/volume/fc/fc_test.go @@ -29,6 +29,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/fake" utiltesting "k8s.io/client-go/util/testing" + "k8s.io/utils/exec/testing" + "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" volumetest "k8s.io/kubernetes/pkg/volume/testing" @@ -164,7 +166,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { fakeManager := newFakeDiskManager() defer fakeManager.Cleanup() fakeMounter := mount.NewFakeMounter(nil) - fakeExec := mount.NewFakeExec(nil) + fakeExec := &testingexec.FakeExec{} mounter, err := plug.(*fcPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter, fakeExec) if err != nil { t.Errorf("Failed to make a new Mounter: %v", err) @@ -227,7 +229,7 @@ func doTestPluginNilMounter(t *testing.T, spec *volume.Spec) { fakeManager := newFakeDiskManager() defer fakeManager.Cleanup() fakeMounter := mount.NewFakeMounter(nil) - fakeExec := mount.NewFakeExec(nil) + fakeExec := &testingexec.FakeExec{} mounter, err := plug.(*fcPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter, fakeExec) if err == nil { t.Errorf("Error failed to make a new Mounter is expected: %v", err) diff --git a/pkg/volume/flexvolume/common_test.go b/pkg/volume/flexvolume/common_test.go index a7ed35b64df..89e85748240 100644 --- a/pkg/volume/flexvolume/common_test.go +++ b/pkg/volume/flexvolume/common_test.go @@ -24,7 +24,7 @@ import ( volumetesting "k8s.io/kubernetes/pkg/volume/testing" "k8s.io/kubernetes/test/utils/harness" "k8s.io/utils/exec" - fakeexec "k8s.io/utils/exec/testing" + "k8s.io/utils/exec/testing" ) func testPlugin(h *harness.Harness) (*flexVolumeAttachablePlugin, string) { @@ -39,7 +39,7 @@ func testPlugin(h *harness.Harness) (*flexVolumeAttachablePlugin, string) { }, rootDir } -func assertDriverCall(t *harness.Harness, output fakeexec.FakeCombinedOutputAction, expectedCommand string, expectedArgs ...string) fakeexec.FakeCommandAction { +func assertDriverCall(t *harness.Harness, output testingexec.FakeCombinedOutputAction, expectedCommand string, expectedArgs ...string) testingexec.FakeCommandAction { return func(cmd string, args ...string) exec.Cmd { if cmd != "/plugin/test" { t.Errorf("Wrong executable called: got %v, expected %v", cmd, "/plugin/test") @@ -51,20 +51,20 @@ func assertDriverCall(t *harness.Harness, output fakeexec.FakeCombinedOutputActi if !sameArgs(cmdArgs, expectedArgs) { t.Errorf("Wrong args for %s: got %v, expected %v", args[0], cmdArgs, expectedArgs) } - return &fakeexec.FakeCmd{ + return &testingexec.FakeCmd{ Argv: args, - CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{output}, + CombinedOutputScript: []testingexec.FakeCombinedOutputAction{output}, } } } -func fakeRunner(fakeCommands ...fakeexec.FakeCommandAction) exec.Interface { - return &fakeexec.FakeExec{ +func fakeRunner(fakeCommands ...testingexec.FakeCommandAction) exec.Interface { + return &testingexec.FakeExec{ CommandScript: fakeCommands, } } -func fakeResultOutput(result interface{}) fakeexec.FakeCombinedOutputAction { +func fakeResultOutput(result interface{}) testingexec.FakeCombinedOutputAction { return func() ([]byte, error) { bytes, err := json.Marshal(result) if err != nil { @@ -74,11 +74,11 @@ func fakeResultOutput(result interface{}) fakeexec.FakeCombinedOutputAction { } } -func successOutput() fakeexec.FakeCombinedOutputAction { +func successOutput() testingexec.FakeCombinedOutputAction { return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true, nil, 0}) } -func notSupportedOutput() fakeexec.FakeCombinedOutputAction { +func notSupportedOutput() testingexec.FakeCombinedOutputAction { return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false, nil, 0}) } diff --git a/pkg/volume/flexvolume/plugin_test.go b/pkg/volume/flexvolume/plugin_test.go index f6afe704aa9..f96dd4a240e 100644 --- a/pkg/volume/flexvolume/plugin_test.go +++ b/pkg/volume/flexvolume/plugin_test.go @@ -20,7 +20,7 @@ import ( "testing" "k8s.io/kubernetes/test/utils/harness" - exec "k8s.io/utils/exec/testing" + "k8s.io/utils/exec/testing" ) func TestInit(tt *testing.T) { @@ -34,7 +34,7 @@ func TestInit(tt *testing.T) { plugin.Init(plugin.host) } -func fakeVolumeNameOutput(name string) exec.FakeCombinedOutputAction { +func fakeVolumeNameOutput(name string) testingexec.FakeCombinedOutputAction { return fakeResultOutput(&DriverStatus{ Status: StatusSuccess, VolumeName: name, diff --git a/pkg/volume/gcepd/attacher.go b/pkg/volume/gcepd/attacher.go index 9a481afce06..be53697f9f7 100644 --- a/pkg/volume/gcepd/attacher.go +++ b/pkg/volume/gcepd/attacher.go @@ -33,6 +33,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog" + utilexec "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" volumeutil "k8s.io/kubernetes/pkg/volume/util" @@ -179,10 +181,10 @@ func (attacher *gcePersistentDiskAttacher) BulkVerifyVolumes(volumesByNode map[t } // search Windows disk number by LUN -func getDiskID(pdName string, exec mount.Exec) (string, error) { +func getDiskID(pdName string, exec utilexec.Interface) (string, error) { // TODO: replace Get-GcePdName with native windows support of Get-Disk, see issue #74674 cmd := `Get-GcePdName | select Name, DeviceId | ConvertTo-Json` - output, err := exec.Run("powershell", "/c", cmd) + output, err := exec.Command("powershell", "/c", cmd).CombinedOutput() if err != nil { klog.Errorf("Get-GcePdName failed, error: %v, output: %q", err, string(output)) err = errors.New(err.Error() + " " + string(output)) diff --git a/pkg/volume/glusterfs/glusterfs.go b/pkg/volume/glusterfs/glusterfs.go index a3b4866a292..0169935df74 100644 --- a/pkg/volume/glusterfs/glusterfs.go +++ b/pkg/volume/glusterfs/glusterfs.go @@ -264,7 +264,7 @@ func (b *glusterfsMounter) CanMount() error { exe := b.plugin.host.GetExec(b.plugin.GetPluginName()) switch runtime.GOOS { case "linux": - if _, err := exe.Run("test", "-x", gciLinuxGlusterMountBinaryPath); err != nil { + if _, err := exe.Command("test", "-x", gciLinuxGlusterMountBinaryPath).CombinedOutput(); err != nil { return fmt.Errorf("required binary %s is missing", gciLinuxGlusterMountBinaryPath) } } diff --git a/pkg/volume/iscsi/BUILD b/pkg/volume/iscsi/BUILD index 48208d1f705..252e40c4926 100644 --- a/pkg/volume/iscsi/BUILD +++ b/pkg/volume/iscsi/BUILD @@ -50,6 +50,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/util/testing:go_default_library", + "//vendor/k8s.io/utils/exec/testing:go_default_library", ], ) diff --git a/pkg/volume/iscsi/iscsi.go b/pkg/volume/iscsi/iscsi.go index 97fa17c0e18..7b5f7039f6d 100644 --- a/pkg/volume/iscsi/iscsi.go +++ b/pkg/volume/iscsi/iscsi.go @@ -27,6 +27,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/klog" + utilexec "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" ioutil "k8s.io/kubernetes/pkg/volume/util" @@ -110,7 +112,7 @@ func (plugin *iscsiPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.V return plugin.newMounterInternal(spec, pod.UID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret) } -func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec, secret map[string]string) (volume.Mounter, error) { +func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface, secret map[string]string) (volume.Mounter, error) { readOnly, fsType, err := getISCSIVolumeInfo(spec) if err != nil { return nil, err @@ -153,7 +155,7 @@ func (plugin *iscsiPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, return plugin.newBlockVolumeMapperInternal(spec, uid, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret) } -func (plugin *iscsiPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec, secret map[string]string) (volume.BlockVolumeMapper, error) { +func (plugin *iscsiPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface, secret map[string]string) (volume.BlockVolumeMapper, error) { readOnly, _, err := getISCSIVolumeInfo(spec) if err != nil { return nil, err @@ -174,7 +176,7 @@ func (plugin *iscsiPlugin) NewUnmounter(volName string, podUID types.UID) (volum return plugin.newUnmounterInternal(volName, podUID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *iscsiPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec) (volume.Unmounter, error) { +func (plugin *iscsiPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.Unmounter, error) { return &iscsiDiskUnmounter{ iscsiDisk: &iscsiDisk{ podUID: podUID, @@ -194,7 +196,7 @@ func (plugin *iscsiPlugin) NewBlockVolumeUnmapper(volName string, podUID types.U return plugin.newUnmapperInternal(volName, podUID, &ISCSIUtil{}, plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *iscsiPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager, exec mount.Exec) (volume.BlockVolumeUnmapper, error) { +func (plugin *iscsiPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager, exec utilexec.Interface) (volume.BlockVolumeUnmapper, error) { return &iscsiDiskUnmapper{ iscsiDisk: &iscsiDisk{ podUID: podUID, @@ -318,7 +320,7 @@ type iscsiDiskMounter struct { fsType string volumeMode v1.PersistentVolumeMode mounter *mount.SafeFormatAndMount - exec mount.Exec + exec utilexec.Interface deviceUtil ioutil.DeviceUtil mountOptions []string } @@ -356,7 +358,7 @@ func (b *iscsiDiskMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) e type iscsiDiskUnmounter struct { *iscsiDisk mounter mount.Interface - exec mount.Exec + exec utilexec.Interface deviceUtil ioutil.DeviceUtil } @@ -376,7 +378,7 @@ func (c *iscsiDiskUnmounter) TearDownAt(dir string) error { type iscsiDiskMapper struct { *iscsiDisk readOnly bool - exec mount.Exec + exec utilexec.Interface deviceUtil ioutil.DeviceUtil } @@ -392,7 +394,7 @@ func (b *iscsiDiskMapper) MapDevice(devicePath, globalMapPath, volumeMapPath, vo type iscsiDiskUnmapper struct { *iscsiDisk - exec mount.Exec + exec utilexec.Interface deviceUtil ioutil.DeviceUtil } diff --git a/pkg/volume/iscsi/iscsi_test.go b/pkg/volume/iscsi/iscsi_test.go index 140bfec8fc3..a200e3100ae 100644 --- a/pkg/volume/iscsi/iscsi_test.go +++ b/pkg/volume/iscsi/iscsi_test.go @@ -27,6 +27,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/fake" utiltesting "k8s.io/client-go/util/testing" + "k8s.io/utils/exec/testing" + "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" volumetest "k8s.io/kubernetes/pkg/volume/testing" @@ -160,7 +162,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { fakeManager := NewFakeDiskManager() defer fakeManager.Cleanup() fakeMounter := mount.NewFakeMounter(nil) - fakeExec := mount.NewFakeExec(nil) + fakeExec := &testingexec.FakeExec{} mounter, err := plug.(*iscsiPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter, fakeExec, nil) if err != nil { t.Errorf("Failed to make a new Mounter: %v", err) diff --git a/pkg/volume/iscsi/iscsi_util.go b/pkg/volume/iscsi/iscsi_util.go index e2367708cbd..3a0ccde3a42 100644 --- a/pkg/volume/iscsi/iscsi_util.go +++ b/pkg/volume/iscsi/iscsi_util.go @@ -24,6 +24,7 @@ import ( "os" "path/filepath" "regexp" + "sort" "strconv" "strings" "time" @@ -96,7 +97,7 @@ func updateISCSIDiscoverydb(b iscsiDiskMounter, tp string) error { v := b.secret[k] if len(v) > 0 { // explicitly not using execWithLog so secrets are not logged - out, err := b.exec.Run("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "update", "-n", k, "-v", v) + out, err := b.exec.Command("iscsiadm", "-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "update", "-n", k, "-v", v).CombinedOutput() if err != nil { return fmt.Errorf("iscsi: failed to update discoverydb key %q error: %v", k, string(out)) } @@ -119,7 +120,7 @@ func updateISCSINode(b iscsiDiskMounter, tp string) error { v := b.secret[k] if len(v) > 0 { // explicitly not using execWithLog so secrets are not logged - out, err := b.exec.Run("iscsiadm", "-m", "node", "-p", tp, "-T", b.Iqn, "-I", b.Iface, "-o", "update", "-n", k, "-v", v) + out, err := b.exec.Command("iscsiadm", "-m", "node", "-p", tp, "-T", b.Iqn, "-I", b.Iface, "-o", "update", "-n", k, "-v", v).CombinedOutput() if err != nil { return fmt.Errorf("iscsi: failed to update node session key %q error: %v", k, string(out)) } @@ -561,7 +562,7 @@ func deleteDevices(c iscsiDiskUnmounter) error { } // Flush any multipath device maps for mpathDevice := range mpathDevices { - _, err = c.exec.Run("multipath", "-f", mpathDevice) + _, err = c.exec.Command("multipath", "-f", mpathDevice).CombinedOutput() if err != nil { klog.Warningf("Warning: Failed to flush multipath device map: %s\nError: %v", mpathDevice, err) // Fall through -- keep deleting the block devices @@ -722,7 +723,7 @@ func (util *ISCSIUtil) DetachBlockISCSIDisk(c iscsiDiskUnmapper, mapPath string) return nil } -func (util *ISCSIUtil) detachISCSIDisk(exec mount.Exec, portals []string, iqn, iface, volName, initiatorName string, found bool) error { +func (util *ISCSIUtil) detachISCSIDisk(exec utilexec.Interface, portals []string, iqn, iface, volName, initiatorName string, found bool) error { for _, portal := range portals { logoutArgs := []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"} deleteArgs := []string{"-m", "node", "-p", portal, "-T", iqn, "-o", "delete"} @@ -731,7 +732,7 @@ func (util *ISCSIUtil) detachISCSIDisk(exec mount.Exec, portals []string, iqn, i deleteArgs = append(deleteArgs, []string{"-I", iface}...) } klog.Infof("iscsi: log out target %s iqn %s iface %s", portal, iqn, iface) - out, err := exec.Run("iscsiadm", logoutArgs...) + out, err := exec.Command("iscsiadm", logoutArgs...).CombinedOutput() err = ignoreExitCodes(err, exit_ISCSI_ERR_NO_OBJS_FOUND, exit_ISCSI_ERR_SESS_NOT_FOUND) if err != nil { klog.Errorf("iscsi: failed to detach disk Error: %s", string(out)) @@ -739,7 +740,7 @@ func (util *ISCSIUtil) detachISCSIDisk(exec mount.Exec, portals []string, iqn, i } // Delete the node record klog.Infof("iscsi: delete node record target %s iqn %s", portal, iqn) - out, err = exec.Run("iscsiadm", deleteArgs...) + out, err = exec.Command("iscsiadm", deleteArgs...).CombinedOutput() err = ignoreExitCodes(err, exit_ISCSI_ERR_NO_OBJS_FOUND, exit_ISCSI_ERR_SESS_NOT_FOUND) if err != nil { klog.Errorf("iscsi: failed to delete node record Error: %s", string(out)) @@ -750,7 +751,7 @@ func (util *ISCSIUtil) detachISCSIDisk(exec mount.Exec, portals []string, iqn, i // If the iface is not created via iscsi plugin, skip to delete if initiatorName != "" && found && iface == (portals[0]+":"+volName) { deleteArgs := []string{"-m", "iface", "-I", iface, "-o", "delete"} - out, err := exec.Run("iscsiadm", deleteArgs...) + out, err := exec.Command("iscsiadm", deleteArgs...).CombinedOutput() err = ignoreExitCodes(err, exit_ISCSI_ERR_NO_OBJS_FOUND, exit_ISCSI_ERR_SESS_NOT_FOUND) if err != nil { klog.Errorf("iscsi: failed to delete iface Error: %s", string(out)) @@ -884,9 +885,15 @@ func cloneIface(b iscsiDiskMounter) error { return lastErr } } + // Get and sort keys to maintain a stable iteration order + var keys []string + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) // update new iface records - for key, val := range params { - _, err = execWithLog(b, "iscsiadm", "-m", "iface", "-I", b.Iface, "-o", "update", "-n", key, "-v", val) + for _, key := range keys { + _, err = execWithLog(b, "iscsiadm", "-m", "iface", "-I", b.Iface, "-o", "update", "-n", key, "-v", params[key]) if err != nil { execWithLog(b, "iscsiadm", "-m", "iface", "-I", b.Iface, "-o", "delete") lastErr = fmt.Errorf("iscsi: failed to update iface records: %s (%v). iface(%s) will be used", out, err, b.InitIface) @@ -965,7 +972,7 @@ func ignoreExitCodes(err error, ignoredExitCodes ...int) error { func execWithLog(b iscsiDiskMounter, cmd string, args ...string) (string, error) { start := time.Now() - out, err := b.exec.Run(cmd, args...) + out, err := b.exec.Command(cmd, args...).CombinedOutput() if klog.V(5) { d := time.Since(start) klog.V(5).Infof("Executed %s %v in %v, err: %v", cmd, args, d, err) diff --git a/pkg/volume/iscsi/iscsi_util_test.go b/pkg/volume/iscsi/iscsi_util_test.go index c8560297711..b6c46713c83 100644 --- a/pkg/volume/iscsi/iscsi_util_test.go +++ b/pkg/volume/iscsi/iscsi_util_test.go @@ -17,16 +17,20 @@ limitations under the License. package iscsi import ( - "errors" - "fmt" "io/ioutil" "os" "path/filepath" "reflect" "testing" - "k8s.io/kubernetes/pkg/util/mount" + "k8s.io/utils/exec/testing" + "k8s.io/kubernetes/pkg/volume" + volumetest "k8s.io/kubernetes/pkg/volume/testing" +) + +const ( + TestIface = "192.168.1.10:pv0001" ) func TestExtractDeviceAndPrefix(t *testing.T) { @@ -216,28 +220,28 @@ func TestParseIscsiadmShow(t *testing.T) { } func TestClonedIface(t *testing.T) { - cmdCount := 0 - fakeExec := mount.NewFakeExec(func(cmd string, args ...string) ([]byte, error) { - cmdCount++ - if cmd != "iscsiadm" { - t.Errorf("iscsiadm command expected, got %q", cmd) - } - switch cmdCount { - case 1: - // iscsiadm -m iface -I -o show - return []byte("iface.ipaddress = \niface.transport_name = tcp\niface.initiatorname = \n"), nil - - case 2: - // iscsiadm -m iface -I -o new - return []byte("New interface 192.168.1.10:pv0001 added"), nil - case 3: - // iscsiadm -m iface -I -o update -n -v - return []byte(""), nil - case 4: - return []byte(""), nil - } - return nil, fmt.Errorf("Unexpected exec call nr %d: %s", cmdCount, cmd) - }) + fakeExec := &testingexec.FakeExec{} + scripts := []volumetest.CommandScript{ + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", "", "-o", "show"}, + Output: "iface.ipaddress = \niface.transport_name = tcp\niface.initiatorname = \n", + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "new"}, + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.initiatorname", "-v", ""}, + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.transport_name", "-v", "tcp"}, + }, + } + volumetest.ScriptCommands(fakeExec, scripts) + fakeExec.ExactOrder = true plugins := []volume.VolumePlugin{ &iscsiPlugin{ host: nil, @@ -246,27 +250,32 @@ func TestClonedIface(t *testing.T) { plugin := plugins[0] fakeMounter := iscsiDiskMounter{ iscsiDisk: &iscsiDisk{ - Iface: "192.168.1.10:pv0001", + Iface: TestIface, plugin: plugin.(*iscsiPlugin)}, exec: fakeExec, } - cloneIface(fakeMounter) - if cmdCount != 4 { - t.Errorf("expected 4 CombinedOutput() calls, got %d", cmdCount) + err := cloneIface(fakeMounter) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if fakeExec.CommandCalls != len(scripts) { + t.Errorf("expected 4 CombinedOutput() calls, got %d", fakeExec.CommandCalls) } - } func TestClonedIfaceShowError(t *testing.T) { - cmdCount := 0 - fakeExec := mount.NewFakeExec(func(cmd string, args ...string) ([]byte, error) { - cmdCount++ - if cmd != "iscsiadm" { - t.Errorf("iscsiadm command expected, got %q", cmd) - } - // iscsiadm -m iface -I -o show, return test error - return []byte(""), errors.New("test error") - }) + fakeExec := &testingexec.FakeExec{} + scripts := []volumetest.CommandScript{ + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", "", "-o", "show"}, + Output: "test error", + ReturnCode: 1, + }, + } + volumetest.ScriptCommands(fakeExec, scripts) + fakeExec.ExactOrder = true + plugins := []volume.VolumePlugin{ &iscsiPlugin{ host: nil, @@ -275,43 +284,49 @@ func TestClonedIfaceShowError(t *testing.T) { plugin := plugins[0] fakeMounter := iscsiDiskMounter{ iscsiDisk: &iscsiDisk{ - Iface: "192.168.1.10:pv0001", + Iface: TestIface, plugin: plugin.(*iscsiPlugin)}, exec: fakeExec, } - cloneIface(fakeMounter) - if cmdCount != 1 { - t.Errorf("expected 1 CombinedOutput() calls, got %d", cmdCount) + err := cloneIface(fakeMounter) + if err == nil { + t.Errorf("expect to receive error, nil received") + } + if fakeExec.CommandCalls != len(scripts) { + t.Errorf("expected 1 CombinedOutput() calls, got %d", fakeExec.CommandCalls) } } func TestClonedIfaceUpdateError(t *testing.T) { - cmdCount := 0 - fakeExec := mount.NewFakeExec(func(cmd string, args ...string) ([]byte, error) { - cmdCount++ - if cmd != "iscsiadm" { - t.Errorf("iscsiadm command expected, got %q", cmd) - } - switch cmdCount { - case 1: - // iscsiadm -m iface -I -o show - return []byte("iface.ipaddress = \niface.transport_name = tcp\niface.initiatorname = \n"), nil + fakeExec := &testingexec.FakeExec{} + scripts := []volumetest.CommandScript{ + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", "", "-o", "show"}, + Output: "iface.ipaddress = \niface.transport_name = tcp\niface.initiatorname = \n", + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "new"}, + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.initiatorname", "-v", ""}, + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "update", "-n", "iface.transport_name", "-v", "tcp"}, + ReturnCode: 1, + }, + { + Cmd: "iscsiadm", + Args: []string{"-m", "iface", "-I", TestIface, "-o", "delete"}, + }, + } + volumetest.ScriptCommands(fakeExec, scripts) + fakeExec.ExactOrder = true - case 2: - // iscsiadm -m iface -I -o new - return []byte("New interface 192.168.1.10:pv0001 added"), nil - case 3: - // iscsiadm -m iface -I -o update -n -v - return []byte(""), nil - case 4: - return []byte(""), errors.New("test error") - case 5: - // iscsiadm -m iface -I -o delete - return []byte(""), nil - } - return nil, fmt.Errorf("Unexpected exec call nr %d: %s", cmdCount, cmd) - }) plugins := []volume.VolumePlugin{ &iscsiPlugin{ host: nil, @@ -320,13 +335,16 @@ func TestClonedIfaceUpdateError(t *testing.T) { plugin := plugins[0] fakeMounter := iscsiDiskMounter{ iscsiDisk: &iscsiDisk{ - Iface: "192.168.1.10:pv0001", + Iface: TestIface, plugin: plugin.(*iscsiPlugin)}, exec: fakeExec, } - cloneIface(fakeMounter) - if cmdCount != 5 { - t.Errorf("expected 5 CombinedOutput() calls, got %d", cmdCount) + err := cloneIface(fakeMounter) + if err == nil { + t.Errorf("expect to receive error, nil received") + } + if fakeExec.CommandCalls != len(scripts) { + t.Errorf("expected 5 CombinedOutput() calls, got %d", fakeExec.CommandCalls) } } diff --git a/pkg/volume/nfs/nfs.go b/pkg/volume/nfs/nfs.go index caf52d56ef0..9a1bb45cb3d 100644 --- a/pkg/volume/nfs/nfs.go +++ b/pkg/volume/nfs/nfs.go @@ -206,15 +206,15 @@ func (nfsMounter *nfsMounter) CanMount() error { exec := nfsMounter.plugin.host.GetExec(nfsMounter.plugin.GetPluginName()) switch runtime.GOOS { case "linux": - if _, err := exec.Run("test", "-x", "/sbin/mount.nfs"); err != nil { + if _, err := exec.Command("test", "-x", "/sbin/mount.nfs").CombinedOutput(); err != nil { return fmt.Errorf("Required binary /sbin/mount.nfs is missing") } - if _, err := exec.Run("test", "-x", "/sbin/mount.nfs4"); err != nil { + if _, err := exec.Command("test", "-x", "/sbin/mount.nfs4").CombinedOutput(); err != nil { return fmt.Errorf("Required binary /sbin/mount.nfs4 is missing") } return nil case "darwin": - if _, err := exec.Run("test", "-x", "/sbin/mount_nfs"); err != nil { + if _, err := exec.Command("test", "-x", "/sbin/mount_nfs").CombinedOutput(); err != nil { return fmt.Errorf("Required binary /sbin/mount_nfs is missing") } } diff --git a/pkg/volume/plugins.go b/pkg/volume/plugins.go index be33716b840..f122a994ef6 100644 --- a/pkg/volume/plugins.go +++ b/pkg/volume/plugins.go @@ -38,6 +38,8 @@ import ( "k8s.io/client-go/tools/record" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog" + "k8s.io/utils/exec" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume/util/hostutil" @@ -437,7 +439,7 @@ type VolumeHost interface { DeleteServiceAccountTokenFunc() func(podUID types.UID) // Returns an interface that should be used to execute any utilities in volume plugins - GetExec(pluginName string) mount.Exec + GetExec(pluginName string) exec.Interface // Returns the labels on the node GetNodeLabels() (map[string]string, error) diff --git a/pkg/volume/quobyte/quobyte.go b/pkg/volume/quobyte/quobyte.go index d996c007fba..5397e8e0be1 100644 --- a/pkg/volume/quobyte/quobyte.go +++ b/pkg/volume/quobyte/quobyte.go @@ -102,7 +102,7 @@ func (plugin *quobytePlugin) CanSupport(spec *volume.Spec) bool { } exec := plugin.host.GetExec(plugin.GetPluginName()) - if out, err := exec.Run("ls", "/sbin/mount.quobyte"); err == nil { + if out, err := exec.Command("ls", "/sbin/mount.quobyte").CombinedOutput(); err == nil { klog.V(4).Infof("quobyte: can support: %s", string(out)) return true } diff --git a/pkg/volume/rbd/BUILD b/pkg/volume/rbd/BUILD index 94917607158..8cdcf9c5ebc 100644 --- a/pkg/volume/rbd/BUILD +++ b/pkg/volume/rbd/BUILD @@ -35,6 +35,7 @@ go_library( "//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/path:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", ], diff --git a/pkg/volume/rbd/rbd.go b/pkg/volume/rbd/rbd.go index 9f127b67c96..91821bbbf4a 100644 --- a/pkg/volume/rbd/rbd.go +++ b/pkg/volume/rbd/rbd.go @@ -38,6 +38,7 @@ import ( "k8s.io/kubernetes/pkg/volume/util" volutil "k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" + utilexec "k8s.io/utils/exec" utilstrings "k8s.io/utils/strings" ) @@ -502,7 +503,7 @@ func (plugin *rbdPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ return plugin.newBlockVolumeMapperInternal(spec, uid, &RBDUtil{}, secret, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *rbdPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string, mounter mount.Interface, exec mount.Exec) (volume.BlockVolumeMapper, error) { +func (plugin *rbdPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string, mounter mount.Interface, exec utilexec.Interface) (volume.BlockVolumeMapper, error) { mon, err := getVolumeSourceMonitors(spec) if err != nil { return nil, err @@ -770,7 +771,7 @@ type rbd struct { ReadOnly bool plugin *rbdPlugin mounter *mount.SafeFormatAndMount - exec mount.Exec + exec utilexec.Interface // Utility interface that provides API calls to the provider to attach/detach disks. manager diskManager volume.MetricsProvider `json:"-"` diff --git a/pkg/volume/rbd/rbd_util.go b/pkg/volume/rbd/rbd_util.go index 09772e1141e..89f1e41b221 100644 --- a/pkg/volume/rbd/rbd_util.go +++ b/pkg/volume/rbd/rbd_util.go @@ -32,7 +32,7 @@ import ( "strings" "time" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/wait" @@ -42,6 +42,7 @@ import ( "k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/pkg/volume" volutil "k8s.io/kubernetes/pkg/volume/util" + utilexec "k8s.io/utils/exec" utilpath "k8s.io/utils/path" ) @@ -227,22 +228,22 @@ func execRbdMap(b rbdMounter, rbdCmd string, mon string) ([]byte, error) { // do not change this format - some tools like rbd-nbd are strict about it. imgPath := fmt.Sprintf("%s/%s", b.Pool, b.Image) if b.Secret != "" { - return b.exec.Run(rbdCmd, - "map", imgPath, "--id", b.Id, "-m", mon, "--key="+b.Secret) + return b.exec.Command(rbdCmd, + "map", imgPath, "--id", b.Id, "-m", mon, "--key="+b.Secret).CombinedOutput() } else { - return b.exec.Run(rbdCmd, - "map", imgPath, "--id", b.Id, "-m", mon, "-k", b.Keyring) + return b.exec.Command(rbdCmd, + "map", imgPath, "--id", b.Id, "-m", mon, "-k", b.Keyring).CombinedOutput() } } // Check if rbd-nbd tools are installed. -func checkRbdNbdTools(e mount.Exec) bool { - _, err := e.Run("modprobe", "nbd") +func checkRbdNbdTools(e utilexec.Interface) bool { + _, err := e.Command("modprobe", "nbd").CombinedOutput() if err != nil { klog.V(5).Infof("rbd-nbd: nbd modprobe failed with error %v", err) return false } - if _, err := e.Run("rbd-nbd", "--version"); err != nil { + if _, err := e.Command("rbd-nbd", "--version").CombinedOutput(); err != nil { klog.V(5).Infof("rbd-nbd: getting rbd-nbd version failed with error %v", err) return false } @@ -335,7 +336,7 @@ func (util *RBDUtil) rbdUnlock(b rbdMounter) error { // Get the locker name, something like "client.1234". args := []string{"lock", "list", b.Image, "--pool", b.Pool, "--id", b.Id, "-m", mon} args = append(args, secret_opt...) - cmd, err = b.exec.Run("rbd", args...) + cmd, err = b.exec.Command("rbd", args...).CombinedOutput() output = string(cmd) klog.V(4).Infof("lock list output %q", output) if err != nil { @@ -353,7 +354,7 @@ func (util *RBDUtil) rbdUnlock(b rbdMounter) error { if len(locker) > 0 { args := []string{"lock", "remove", b.Image, lock_id, locker, "--pool", b.Pool, "--id", b.Id, "-m", mon} args = append(args, secret_opt...) - cmd, err = b.exec.Run("rbd", args...) + cmd, err = b.exec.Command("rbd", args...).CombinedOutput() if err == nil { klog.V(4).Infof("rbd: successfully remove lock (locker_id: %s) on image: %s/%s with id %s mon %s", lock_id, b.Pool, b.Image, b.Id, mon) } else { @@ -432,7 +433,7 @@ func (util *RBDUtil) AttachDisk(b rbdMounter) (string, error) { mon := util.kernelRBDMonitorsOpt(b.Mon) klog.V(1).Infof("rbd: map mon %s", mon) - _, err := b.exec.Run("modprobe", "rbd") + _, err := b.exec.Command("modprobe", "rbd").CombinedOutput() if err != nil { klog.Warningf("rbd: failed to load rbd kernel module:%v", err) } @@ -483,7 +484,7 @@ func (util *RBDUtil) DetachDisk(plugin *rbdPlugin, deviceMountPath string, devic } // rbd unmap - output, err := exec.Run(rbdCmd, "unmap", device) + output, err := exec.Command(rbdCmd, "unmap", device).CombinedOutput() if err != nil { return rbdErrors(err, fmt.Errorf("rbd: failed to unmap device %s, error %v, rbd output: %v", device, err, output)) } @@ -542,7 +543,7 @@ func (util *RBDUtil) DetachBlockDisk(disk rbdDiskUnmapper, mapPath string) error } // rbd unmap - output, err := exec.Run(rbdCmd, "unmap", device) + output, err := exec.Command(rbdCmd, "unmap", device).CombinedOutput() if err != nil { return rbdErrors(err, fmt.Errorf("rbd: failed to unmap device %s, error %v, rbd output: %s", device, err, string(output))) } @@ -601,7 +602,7 @@ func (util *RBDUtil) CreateImage(p *rbdVolumeProvisioner) (r *v1.RBDPersistentVo features := strings.Join(p.rbdMounter.imageFeatures, ",") args = append(args, "--image-feature", features) } - output, err = p.exec.Run("rbd", args...) + output, err = p.exec.Command("rbd", args...).CombinedOutput() if err != nil { klog.Warningf("failed to create rbd image, output %v", string(output)) @@ -628,8 +629,8 @@ func (util *RBDUtil) DeleteImage(p *rbdVolumeDeleter) error { // rbd rm. mon := util.kernelRBDMonitorsOpt(p.rbdMounter.Mon) klog.V(4).Infof("rbd: rm %s using mon %s, pool %s id %s key %s", p.rbdMounter.Image, mon, p.rbdMounter.Pool, p.rbdMounter.adminId, p.rbdMounter.adminSecret) - output, err = p.exec.Run("rbd", - "rm", p.rbdMounter.Image, "--pool", p.rbdMounter.Pool, "--id", p.rbdMounter.adminId, "-m", mon, "--key="+p.rbdMounter.adminSecret) + output, err = p.exec.Command("rbd", + "rm", p.rbdMounter.Image, "--pool", p.rbdMounter.Pool, "--id", p.rbdMounter.adminId, "-m", mon, "--key="+p.rbdMounter.adminSecret).CombinedOutput() if err == nil { return nil } @@ -660,8 +661,8 @@ func (util *RBDUtil) ExpandImage(rbdExpander *rbdVolumeExpander, oldSize resourc // rbd resize. mon := util.kernelRBDMonitorsOpt(rbdExpander.rbdMounter.Mon) klog.V(4).Infof("rbd: resize %s using mon %s, pool %s id %s key %s", rbdExpander.rbdMounter.Image, mon, rbdExpander.rbdMounter.Pool, rbdExpander.rbdMounter.adminId, rbdExpander.rbdMounter.adminSecret) - output, err = rbdExpander.exec.Run("rbd", - "resize", rbdExpander.rbdMounter.Image, "--size", newVolSz, "--pool", rbdExpander.rbdMounter.Pool, "--id", rbdExpander.rbdMounter.adminId, "-m", mon, "--key="+rbdExpander.rbdMounter.adminSecret) + output, err = rbdExpander.exec.Command("rbd", + "resize", rbdExpander.rbdMounter.Image, "--size", newVolSz, "--pool", rbdExpander.rbdMounter.Pool, "--id", rbdExpander.rbdMounter.adminId, "-m", mon, "--key="+rbdExpander.rbdMounter.adminSecret).CombinedOutput() if err == nil { return newSizeQuant, nil } @@ -702,8 +703,8 @@ func (util *RBDUtil) rbdInfo(b *rbdMounter) (int, error) { // rbd: error opening image 1234: (2) No such file or directory // klog.V(4).Infof("rbd: info %s using mon %s, pool %s id %s key %s", b.Image, mon, b.Pool, id, secret) - output, err = b.exec.Run("rbd", - "info", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret, "-k=/dev/null", "--format=json") + output, err = b.exec.Command("rbd", + "info", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret, "-k=/dev/null", "--format=json").CombinedOutput() if err, ok := err.(*exec.Error); ok { if err.Err == exec.ErrNotFound { @@ -765,8 +766,8 @@ func (util *RBDUtil) rbdStatus(b *rbdMounter) (bool, string, error) { // rbd: error opening image kubernetes-dynamic-pvc-: (2) No such file or directory // klog.V(4).Infof("rbd: status %s using mon %s, pool %s id %s key %s", b.Image, mon, b.Pool, id, secret) - cmd, err = b.exec.Run("rbd", - "status", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret) + cmd, err = b.exec.Command("rbd", + "status", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret).CombinedOutput() output = string(cmd) if err, ok := err.(*exec.Error); ok { diff --git a/pkg/volume/scaleio/BUILD b/pkg/volume/scaleio/BUILD index c9b54e12734..fd96f3a6f8b 100644 --- a/pkg/volume/scaleio/BUILD +++ b/pkg/volume/scaleio/BUILD @@ -15,7 +15,6 @@ go_test( ], embed = [":go_default_library"], deps = [ - "//pkg/util/mount:go_default_library", "//pkg/volume:go_default_library", "//pkg/volume/testing:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", @@ -26,6 +25,7 @@ go_test( "//staging/src/k8s.io/client-go/util/testing:go_default_library", "//vendor/github.com/thecodeteam/goscaleio/types/v1:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec/testing:go_default_library", ], ) @@ -52,6 +52,7 @@ go_library( "//vendor/github.com/thecodeteam/goscaleio:go_default_library", "//vendor/github.com/thecodeteam/goscaleio/types/v1:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/keymutex:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", ], diff --git a/pkg/volume/scaleio/sio_client.go b/pkg/volume/scaleio/sio_client.go index bc9b9868f7b..443a56f178c 100644 --- a/pkg/volume/scaleio/sio_client.go +++ b/pkg/volume/scaleio/sio_client.go @@ -28,7 +28,7 @@ import ( "sync" "time" - "k8s.io/kubernetes/pkg/util/mount" + utilexec "k8s.io/utils/exec" sio "github.com/thecodeteam/goscaleio" siotypes "github.com/thecodeteam/goscaleio/types/v1" @@ -78,10 +78,10 @@ type sioClient struct { inited bool diskRegex *regexp.Regexp mtx sync.Mutex - exec mount.Exec + exec utilexec.Interface } -func newSioClient(gateway, username, password string, sslEnabled bool, exec mount.Exec) (*sioClient, error) { +func newSioClient(gateway, username, password string, sslEnabled bool, exec utilexec.Interface) (*sioClient, error) { client := new(sioClient) client.gateway = gateway client.username = username @@ -321,7 +321,7 @@ func (c *sioClient) getGUID() (string, error) { if c.sdcGUID == "" { klog.V(4).Info(log("sdc guid label not set, falling back to using drv_cfg")) cmd := c.getSdcCmd() - output, err := c.exec.Run(cmd, "--query_guid") + output, err := c.exec.Command(cmd, "--query_guid").CombinedOutput() if err != nil { klog.Error(log("drv_cfg --query_guid failed: %v", err)) return "", err diff --git a/pkg/volume/scaleio/sio_mgr.go b/pkg/volume/scaleio/sio_mgr.go index 1dc662a4c85..fa90311c5a2 100644 --- a/pkg/volume/scaleio/sio_mgr.go +++ b/pkg/volume/scaleio/sio_mgr.go @@ -20,9 +20,8 @@ import ( "errors" "strconv" - "k8s.io/kubernetes/pkg/util/mount" - "k8s.io/klog" + utilexec "k8s.io/utils/exec" siotypes "github.com/thecodeteam/goscaleio/types/v1" ) @@ -30,10 +29,10 @@ import ( type sioMgr struct { client sioInterface configData map[string]string - exec mount.Exec + exec utilexec.Interface } -func newSioMgr(configs map[string]string, exec mount.Exec) (*sioMgr, error) { +func newSioMgr(configs map[string]string, exec utilexec.Interface) (*sioMgr, error) { if configs == nil { return nil, errors.New("missing configuration data") } diff --git a/pkg/volume/scaleio/sio_mgr_test.go b/pkg/volume/scaleio/sio_mgr_test.go index 1815a5cba15..024750b03db 100644 --- a/pkg/volume/scaleio/sio_mgr_test.go +++ b/pkg/volume/scaleio/sio_mgr_test.go @@ -21,9 +21,8 @@ import ( "testing" "time" - "k8s.io/kubernetes/pkg/util/mount" - siotypes "github.com/thecodeteam/goscaleio/types/v1" + "k8s.io/utils/exec/testing" ) var ( @@ -44,7 +43,7 @@ var ( ) func newTestMgr(t *testing.T) *sioMgr { - mgr, err := newSioMgr(fakeConfig, mount.NewFakeExec(nil)) + mgr, err := newSioMgr(fakeConfig, &testingexec.FakeExec{}) if err != nil { t.Error(err) } @@ -53,7 +52,7 @@ func newTestMgr(t *testing.T) *sioMgr { } func TestMgrNew(t *testing.T) { - mgr, err := newSioMgr(fakeConfig, mount.NewFakeExec(nil)) + mgr, err := newSioMgr(fakeConfig, &testingexec.FakeExec{}) if err != nil { t.Fatal(err) } diff --git a/pkg/volume/storageos/BUILD b/pkg/volume/storageos/BUILD index db9aeeb6dec..9b0f4e7dc95 100644 --- a/pkg/volume/storageos/BUILD +++ b/pkg/volume/storageos/BUILD @@ -27,6 +27,7 @@ go_library( "//vendor/github.com/storageos/go-api:go_default_library", "//vendor/github.com/storageos/go-api/types:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", ], ) @@ -48,6 +49,7 @@ go_test( "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/util/testing:go_default_library", "//vendor/github.com/storageos/go-api/types:go_default_library", + "//vendor/k8s.io/utils/exec/testing:go_default_library", ], ) diff --git a/pkg/volume/storageos/storageos.go b/pkg/volume/storageos/storageos.go index a126d50799a..8350b44b786 100644 --- a/pkg/volume/storageos/storageos.go +++ b/pkg/volume/storageos/storageos.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util" + utilexec "k8s.io/utils/exec" utilstrings "k8s.io/utils/strings" ) @@ -115,7 +116,7 @@ func (plugin *storageosPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volu return plugin.newMounterInternal(spec, pod, apiCfg, &storageosUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *storageosPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, apiCfg *storageosAPIConfig, manager storageosManager, mounter mount.Interface, exec mount.Exec) (volume.Mounter, error) { +func (plugin *storageosPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, apiCfg *storageosAPIConfig, manager storageosManager, mounter mount.Interface, exec utilexec.Interface) (volume.Mounter, error) { volName, volNamespace, fsType, readOnly, err := getVolumeInfoFromSpec(spec) if err != nil { @@ -147,7 +148,7 @@ func (plugin *storageosPlugin) NewUnmounter(pvName string, podUID types.UID) (vo return plugin.newUnmounterInternal(pvName, podUID, &storageosUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) } -func (plugin *storageosPlugin) newUnmounterInternal(pvName string, podUID types.UID, manager storageosManager, mounter mount.Interface, exec mount.Exec) (volume.Unmounter, error) { +func (plugin *storageosPlugin) newUnmounterInternal(pvName string, podUID types.UID, manager storageosManager, mounter mount.Interface, exec utilexec.Interface) (volume.Unmounter, error) { // Parse volume namespace & name from mountpoint if mounted volNamespace, volName, err := getVolumeInfo(pvName, podUID, plugin.host) @@ -311,7 +312,7 @@ type storageos struct { apiCfg *storageosAPIConfig manager storageosManager mounter mount.Interface - exec mount.Exec + exec utilexec.Interface plugin *storageosPlugin volume.MetricsProvider } diff --git a/pkg/volume/storageos/storageos_test.go b/pkg/volume/storageos/storageos_test.go index bf30487f20b..43229bed236 100644 --- a/pkg/volume/storageos/storageos_test.go +++ b/pkg/volume/storageos/storageos_test.go @@ -30,6 +30,7 @@ import ( "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" volumetest "k8s.io/kubernetes/pkg/volume/testing" + "k8s.io/utils/exec/testing" ) func TestCanSupport(t *testing.T) { @@ -194,7 +195,7 @@ func TestPlugin(t *testing.T) { t.Errorf("Couldn't get secret from %v/%v", pod.Namespace, secretName) } - mounter, err := plug.(*storageosPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, apiCfg, fakeManager, mount.NewFakeMounter(nil), mount.NewFakeExec(nil)) + mounter, err := plug.(*storageosPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, apiCfg, fakeManager, mount.NewFakeMounter(nil), &testingexec.FakeExec{}) if err != nil { t.Fatalf("Failed to make a new Mounter: %v", err) } @@ -231,7 +232,7 @@ func TestPlugin(t *testing.T) { // Test Unmounter fakeManager = &fakePDManager{} - unmounter, err := plug.(*storageosPlugin).newUnmounterInternal("vol1-pvname", types.UID("poduid"), fakeManager, mount.NewFakeMounter(nil), mount.NewFakeExec(nil)) + unmounter, err := plug.(*storageosPlugin).newUnmounterInternal("vol1-pvname", types.UID("poduid"), fakeManager, mount.NewFakeMounter(nil), &testingexec.FakeExec{}) if err != nil { t.Errorf("Failed to make a new Unmounter: %v", err) } @@ -372,7 +373,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { fakeManager := &fakePDManager{} fakeConfig := &fakeConfig{} apiCfg := fakeConfig.GetAPIConfig() - mounter, err := plug.(*storageosPlugin).newMounterInternal(spec, pod, apiCfg, fakeManager, mount.NewFakeMounter(nil), mount.NewFakeExec(nil)) + mounter, err := plug.(*storageosPlugin).newMounterInternal(spec, pod, apiCfg, fakeManager, mount.NewFakeMounter(nil), &testingexec.FakeExec{}) if err != nil { t.Fatalf("error creating a new internal mounter:%v", err) } diff --git a/pkg/volume/storageos/storageos_util.go b/pkg/volume/storageos/storageos_util.go index c7f430ea5d4..05dcf039c14 100644 --- a/pkg/volume/storageos/storageos_util.go +++ b/pkg/volume/storageos/storageos_util.go @@ -23,11 +23,10 @@ import ( "path/filepath" "strings" - "k8s.io/kubernetes/pkg/util/mount" - storageosapi "github.com/storageos/go-api" storageostypes "github.com/storageos/go-api/types" "k8s.io/klog" + utilexec "k8s.io/utils/exec" ) const ( @@ -331,7 +330,7 @@ func pathDeviceType(path string) (deviceType, error) { // attachFileDevice takes a path to a regular file and makes it available as an // attached block device. -func attachFileDevice(path string, exec mount.Exec) (string, error) { +func attachFileDevice(path string, exec utilexec.Interface) (string, error) { blockDevicePath, err := getLoopDevice(path, exec) if err != nil && err.Error() != ErrDeviceNotFound { return "", err @@ -349,7 +348,7 @@ func attachFileDevice(path string, exec mount.Exec) (string, error) { } // Returns the full path to the loop device associated with the given path. -func getLoopDevice(path string, exec mount.Exec) (string, error) { +func getLoopDevice(path string, exec utilexec.Interface) (string, error) { _, err := os.Stat(path) if os.IsNotExist(err) { return "", errors.New(ErrNotAvailable) @@ -359,7 +358,7 @@ func getLoopDevice(path string, exec mount.Exec) (string, error) { } args := []string{"-j", path} - out, err := exec.Run(losetupPath, args...) + out, err := exec.Command(losetupPath, args...).CombinedOutput() if err != nil { klog.V(2).Infof("Failed device discover command for path %s: %v", path, err) return "", err @@ -367,9 +366,9 @@ func getLoopDevice(path string, exec mount.Exec) (string, error) { return parseLosetupOutputForDevice(out) } -func makeLoopDevice(path string, exec mount.Exec) (string, error) { +func makeLoopDevice(path string, exec utilexec.Interface) (string, error) { args := []string{"-f", "-P", "--show", path} - out, err := exec.Run(losetupPath, args...) + out, err := exec.Command(losetupPath, args...).CombinedOutput() if err != nil { klog.V(2).Infof("Failed device create command for path %s: %v", path, err) return "", err @@ -377,9 +376,9 @@ func makeLoopDevice(path string, exec mount.Exec) (string, error) { return parseLosetupOutputForDevice(out) } -func removeLoopDevice(device string, exec mount.Exec) error { +func removeLoopDevice(device string, exec utilexec.Interface) error { args := []string{"-d", device} - out, err := exec.Run(losetupPath, args...) + out, err := exec.Command(losetupPath, args...).CombinedOutput() if err != nil { if !strings.Contains(string(out), "No such device or address") { return err diff --git a/pkg/volume/testing/BUILD b/pkg/volume/testing/BUILD index e666a1b6a13..2bbc87a9014 100644 --- a/pkg/volume/testing/BUILD +++ b/pkg/volume/testing/BUILD @@ -35,6 +35,8 @@ go_library( "//staging/src/k8s.io/client-go/util/testing:go_default_library", "//staging/src/k8s.io/cloud-provider:go_default_library", "//vendor/github.com/stretchr/testify/mock:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", + "//vendor/k8s.io/utils/exec/testing:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", ], ) diff --git a/pkg/volume/testing/testing.go b/pkg/volume/testing/testing.go index 28301b7a46d..b34d977da0c 100644 --- a/pkg/volume/testing/testing.go +++ b/pkg/volume/testing/testing.go @@ -20,7 +20,6 @@ import ( "fmt" "net" "os" - "os/exec" "path/filepath" "strings" "sync" @@ -41,6 +40,9 @@ import ( "k8s.io/client-go/tools/record" utiltesting "k8s.io/client-go/util/testing" cloudprovider "k8s.io/cloud-provider" + "k8s.io/utils/exec" + "k8s.io/utils/exec/testing" + "k8s.io/kubernetes/pkg/util/mount" . "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util" @@ -73,7 +75,7 @@ type fakeVolumeHost struct { cloud cloudprovider.Interface mounter mount.Interface hostUtil hostutil.HostUtils - exec mount.Exec + exec *testingexec.FakeExec nodeLabels map[string]string nodeName string subpather subpath.Interface @@ -111,7 +113,7 @@ func newFakeVolumeHost(rootDir string, kubeClient clientset.Interface, plugins [ host := &fakeVolumeHost{rootDir: rootDir, kubeClient: kubeClient, cloud: cloud} host.mounter = mount.NewFakeMounter(nil) host.hostUtil = hostutil.NewFakeHostUtil(pathToTypeMap) - host.exec = mount.NewFakeExec(nil) + host.exec = &testingexec.FakeExec{DisableScripts: true} host.pluginMgr.InitPlugins(plugins, nil /* prober */, host) host.subpather = &subpath.FakeSubpath{} host.informerFactory = informers.NewSharedInformerFactory(kubeClient, time.Minute) @@ -213,7 +215,7 @@ func (f *fakeVolumeHost) GetSecretFunc() func(namespace, name string) (*v1.Secre } } -func (f *fakeVolumeHost) GetExec(pluginName string) mount.Exec { +func (f *fakeVolumeHost) GetExec(pluginName string) exec.Interface { return f.exec } @@ -248,6 +250,64 @@ func (f *fakeVolumeHost) GetEventRecorder() record.EventRecorder { return nil } +func (f *fakeVolumeHost) ScriptCommands(scripts []CommandScript) { + ScriptCommands(f.exec, scripts) +} + +// CommandScript is used to pre-configure a command that will be executed and +// optionally set it's output (stdout and stderr combined) and return code. +type CommandScript struct { + // Cmd is the command to execute, e.g. "ls" + Cmd string + // Args is a slice of arguments to pass to the command, e.g. "-a" + Args []string + // Output is the combined stdout and stderr of the command to return + Output string + // ReturnCode is the exit code for the command. Setting this to non-zero will + // cause the command to return an error with this exit code set. + ReturnCode int +} + +// ScriptCommands configures fe, the FakeExec, to have a pre-configured list of +// commands to expect. Calling more commands using fe than those scripted will +// result in a panic. By default, the fe does not enforce command argument checking +// or order -- if you have given an Output to the command, the first command scripted +// will return its output on the first command call, even if the command called is +// different than the one scripted. This is mostly useful to make sure that the +// right number of commands were called. If you want to check the exact commands +// and arguments were called, set fe.ExectOrder to true. +func ScriptCommands(fe *testingexec.FakeExec, scripts []CommandScript) { + fe.DisableScripts = false + for _, script := range scripts { + fakeCmd := &testingexec.FakeCmd{} + cmdAction := makeFakeCmd(fakeCmd, script.Cmd, script.Args...) + outputAction := makeFakeOutput(script.Output, script.ReturnCode) + fakeCmd.CombinedOutputScript = append(fakeCmd.CombinedOutputScript, outputAction) + fe.CommandScript = append(fe.CommandScript, cmdAction) + } +} + +func makeFakeCmd(fakeCmd *testingexec.FakeCmd, cmd string, args ...string) testingexec.FakeCommandAction { + fc := fakeCmd + c := cmd + a := args + return func(cmd string, args ...string) exec.Cmd { + command := testingexec.InitFakeCmd(fc, c, a...) + return command + } +} + +func makeFakeOutput(output string, rc int) testingexec.FakeCombinedOutputAction { + o := output + var e error + if rc != 0 { + e = testingexec.FakeExitError{Status: rc} + } + return func() ([]byte, error) { + return []byte(o), e + } +} + func ProbeVolumePlugins(config VolumeConfig) []VolumePlugin { if _, ok := config.OtherAttributes["fake-property"]; ok { return []VolumePlugin{ @@ -1131,7 +1191,7 @@ func FindEmptyDirectoryUsageOnTmpfs() (*resource.Quantity, error) { return nil, err } defer os.RemoveAll(tmpDir) - out, err := exec.Command("nice", "-n", "19", "du", "-x", "-s", "-B", "1", tmpDir).CombinedOutput() + out, err := exec.New().Command("nice", "-n", "19", "du", "-x", "-s", "-B", "1", tmpDir).CombinedOutput() if err != nil { return nil, fmt.Errorf("failed command 'du' on %s with error %v", tmpDir, err) } diff --git a/pkg/volume/util/BUILD b/pkg/volume/util/BUILD index 258de55f948..705296fd66c 100644 --- a/pkg/volume/util/BUILD +++ b/pkg/volume/util/BUILD @@ -44,6 +44,7 @@ go_library( "//staging/src/k8s.io/component-base/metrics:go_default_library", "//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library", ], ) diff --git a/pkg/volume/util/util.go b/pkg/volume/util/util.go index 72352feb1ec..42fea454baf 100644 --- a/pkg/volume/util/util.go +++ b/pkg/volume/util/util.go @@ -36,6 +36,7 @@ import ( utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" "k8s.io/klog" + "k8s.io/kubernetes/pkg/api/legacyscheme" podutil "k8s.io/kubernetes/pkg/api/v1/pod" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" @@ -44,6 +45,7 @@ import ( "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util/types" "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" + utilexec "k8s.io/utils/exec" utilstrings "k8s.io/utils/strings" ) @@ -585,11 +587,11 @@ func HasMountRefs(mountPath string, mountRefs []string) bool { } //WriteVolumeCache flush disk data given the spcified mount path -func WriteVolumeCache(deviceMountPath string, exec mount.Exec) error { +func WriteVolumeCache(deviceMountPath string, exec utilexec.Interface) error { // If runtime os is windows, execute Write-VolumeCache powershell command on the disk if runtime.GOOS == "windows" { cmd := fmt.Sprintf("Get-Volume -FilePath %s | Write-Volumecache", deviceMountPath) - output, err := exec.Run("powershell", "/c", cmd) + output, err := exec.Command("powershell", "/c", cmd).CombinedOutput() klog.Infof("command (%q) execeuted: %v, output: %q", cmd, err, string(output)) if err != nil { return fmt.Errorf("command (%q) failed: %v, output: %q", cmd, err, string(output))