Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Revert #114605: its unit test requires root permission" #115769

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 31 additions & 30 deletions staging/src/k8s.io/mount-utils/mount_linux.go
Expand Up @@ -363,31 +363,19 @@ func (mounter *Mounter) Unmount(target string) error {
command := exec.Command("umount", target)
output, err := command.CombinedOutput()
if err != nil {
if err.Error() == errNoChildProcesses {
if command.ProcessState.Success() {
// We don't consider errNoChildProcesses an error if the process itself succeeded (see - k/k issue #103753).
return nil
}
// Rewrite err with the actual exit error of the process.
err = &exec.ExitError{ProcessState: command.ProcessState}
}
if mounter.withSafeNotMountedBehavior && strings.Contains(string(output), errNotMounted) {
klog.V(4).Infof("ignoring 'not mounted' error for %s", target)
return nil
}
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", err, target, string(output))
return checkUmountError(target, command, output, err, mounter.withSafeNotMountedBehavior)
}
return nil
}

// UnmountWithForce unmounts given target but will retry unmounting with force option
// after given timeout.
func (mounter *Mounter) UnmountWithForce(target string, umountTimeout time.Duration) error {
err := tryUnmount(mounter, target, umountTimeout)
err := tryUnmount(target, mounter.withSafeNotMountedBehavior, umountTimeout)
if err != nil {
if err == context.DeadlineExceeded {
klog.V(2).Infof("Timed out waiting for unmount of %s, trying with -f", target)
err = forceUmount(target)
err = forceUmount(target, mounter.withSafeNotMountedBehavior)
}
return err
}
Expand Down Expand Up @@ -799,36 +787,49 @@ func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
}

// tryUnmount calls plain "umount" and waits for unmountTimeout for it to finish.
func tryUnmount(mounter *Mounter, path string, unmountTimeout time.Duration) error {
klog.V(4).Infof("Unmounting %s", path)
func tryUnmount(target string, withSafeNotMountedBehavior bool, unmountTimeout time.Duration) error {
klog.V(4).Infof("Unmounting %s", target)
ctx, cancel := context.WithTimeout(context.Background(), unmountTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, "umount", path)
out, cmderr := cmd.CombinedOutput()
command := exec.CommandContext(ctx, "umount", target)
output, err := command.CombinedOutput()

// CombinedOutput() does not return DeadlineExceeded, make sure it's
// propagated on timeout.
if ctx.Err() != nil {
return ctx.Err()
}

if cmderr != nil {
if mounter.withSafeNotMountedBehavior && strings.Contains(string(out), errNotMounted) {
klog.V(4).Infof("ignoring 'not mounted' error for %s", path)
return nil
}
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", cmderr, path, string(out))
if err != nil {
return checkUmountError(target, command, output, err, withSafeNotMountedBehavior)
}
return nil
}

func forceUmount(path string) error {
cmd := exec.Command("umount", "-f", path)
out, cmderr := cmd.CombinedOutput()
func forceUmount(target string, withSafeNotMountedBehavior bool) error {
command := exec.Command("umount", "-f", target)
output, err := command.CombinedOutput()

if cmderr != nil {
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", cmderr, path, string(out))
if err != nil {
return checkUmountError(target, command, output, err, withSafeNotMountedBehavior)
}
return nil
}

// checkUmountError checks a result of umount command and determine a return value.
func checkUmountError(target string, command *exec.Cmd, output []byte, err error, withSafeNotMountedBehavior bool) error {
if err.Error() == errNoChildProcesses {
if command.ProcessState.Success() {
// We don't consider errNoChildProcesses an error if the process itself succeeded (see - k/k issue #103753).
return nil
}
// Rewrite err with the actual exit error of the process.
err = &exec.ExitError{ProcessState: command.ProcessState}
}
if withSafeNotMountedBehavior && strings.Contains(string(output), errNotMounted) {
klog.V(4).Infof("ignoring 'not mounted' error for %s", target)
return nil
}
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", err, target, string(output))
}
42 changes: 42 additions & 0 deletions staging/src/k8s.io/mount-utils/mount_linux_test.go
Expand Up @@ -24,6 +24,7 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"reflect"
"strings"
"sync"
Expand Down Expand Up @@ -607,6 +608,47 @@ func TestDetectSafeNotMountedBehavior(t *testing.T) {
}
}

func TestCheckUmountError(t *testing.T) {
target := "/test/path"
mochizuki875 marked this conversation as resolved.
Show resolved Hide resolved
withSafeNotMountedBehavior := true
command := exec.Command("uname", "-r") // dummy command return status 0

if err := command.Run(); err != nil {
t.Errorf("Faild to exec dummy command. err: %s", err)
}

testcases := []struct {
output []byte
err error
expected bool
}{
{
err: errors.New("wait: no child processes"),
expected: true,
},
{
output: []byte("umount: /test/path: not mounted."),
err: errors.New("exit status 1"),
expected: true,
},
{
output: []byte("umount: /test/path: No such file or directory"),
err: errors.New("exit status 1"),
expected: false,
},
}

for _, v := range testcases {
if err := checkUmountError(target, command, v.output, v.err, withSafeNotMountedBehavior); (err == nil) != v.expected {
if v.expected {
t.Errorf("Expected to return nil, but did not. err: %s", err)
} else {
t.Errorf("Expected to return error, but did not.")
}
}
}
}

func TestFormat(t *testing.T) {
const (
formatCount = 5
Expand Down