Fix bug:Kubelet failure to umount mount points

This commit is contained in:
linyouchong 2017-12-25 17:36:59 +08:00
parent f5f6f3e715
commit 19003486bf
3 changed files with 97 additions and 24 deletions

View File

@ -147,15 +147,12 @@ go_test(
deps = [ deps = [
"//pkg/apis/core/install:go_default_library", "//pkg/apis/core/install:go_default_library",
"//pkg/apis/core/v1/helper:go_default_library", "//pkg/apis/core/v1/helper:go_default_library",
"//pkg/util/mount:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
] + select({ "//vendor/k8s.io/client-go/util/testing:go_default_library",
"@io_bazel_rules_go//go/platform:linux": [ ],
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
"//conditions:default": [],
}),
) )
filegroup( filegroup(

View File

@ -23,6 +23,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
@ -96,29 +97,42 @@ func UnmountPath(mountPath string, mounter mount.Interface) error {
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint. // IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
// IsNotMountPoint is more expensive but properly handles bind mounts. // IsNotMountPoint is more expensive but properly handles bind mounts.
func UnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMountPointCheck bool) error { func UnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMountPointCheck bool) error {
if pathExists, pathErr := PathExists(mountPath); pathErr != nil { pathExists, pathErr := PathExists(mountPath)
return fmt.Errorf("Error checking if path exists: %v", pathErr) if !pathExists {
} else if !pathExists {
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath) glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
return nil return nil
} }
corruptedMnt := isCorruptedMnt(pathErr)
var notMnt bool if pathErr != nil && !corruptedMnt {
var err error return fmt.Errorf("Error checking path: %v", pathErr)
if extensiveMountPointCheck {
notMnt, err = mount.IsNotMountPoint(mounter, mountPath)
} else {
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
} }
return doUnmountMountPoint(mountPath, mounter, extensiveMountPointCheck, corruptedMnt)
}
if err != nil { // doUnmountMountPoint is a common unmount routine that unmounts the given path and
return err // deletes the remaining directory if successful.
} // if extensiveMountPointCheck is true
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
// IsNotMountPoint is more expensive but properly handles bind mounts.
// if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, Take it as an argument for convenience of testing
func doUnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMountPointCheck bool, corruptedMnt bool) error {
if !corruptedMnt {
var notMnt bool
var err error
if extensiveMountPointCheck {
notMnt, err = mount.IsNotMountPoint(mounter, mountPath)
} else {
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
}
if notMnt { if err != nil {
glog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath) return err
return os.Remove(mountPath) }
if notMnt {
glog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
return os.Remove(mountPath)
}
} }
// Unmount the mount path // Unmount the mount path
@ -128,7 +142,7 @@ func UnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMount
} }
notMnt, mntErr := mounter.IsLikelyNotMountPoint(mountPath) notMnt, mntErr := mounter.IsLikelyNotMountPoint(mountPath)
if mntErr != nil { if mntErr != nil {
return err return mntErr
} }
if notMnt { if notMnt {
glog.V(4).Infof("%q is unmounted, deleting the directory", mountPath) glog.V(4).Infof("%q is unmounted, deleting the directory", mountPath)
@ -144,11 +158,32 @@ func PathExists(path string) (bool, error) {
return true, nil return true, nil
} else if os.IsNotExist(err) { } else if os.IsNotExist(err) {
return false, nil return false, nil
} else if isCorruptedMnt(err) {
return true, err
} else { } else {
return false, err return false, err
} }
} }
// isCorruptedMnt return true if err is about corrupted mount point
func isCorruptedMnt(err error) bool {
if err == nil {
return false
}
var underlyingError error
switch pe := err.(type) {
case nil:
return false
case *os.PathError:
underlyingError = pe.Err
case *os.LinkError:
underlyingError = pe.Err
case *os.SyscallError:
underlyingError = pe.Err
}
return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE
}
// GetSecretForPod locates secret by name in the pod's namespace and returns secret map // GetSecretForPod locates secret by name in the pod's namespace and returns secret map
func GetSecretForPod(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (map[string]string, error) { func GetSecretForPod(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (map[string]string, error) {
secret := make(map[string]string) secret := make(map[string]string)

View File

@ -24,10 +24,12 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utiltesting "k8s.io/client-go/util/testing"
// util.go uses api.Codecs.LegacyCodec so import this package to do some // util.go uses api.Codecs.LegacyCodec so import this package to do some
// resource initialization. // resource initialization.
_ "k8s.io/kubernetes/pkg/apis/core/install" _ "k8s.io/kubernetes/pkg/apis/core/install"
"k8s.io/kubernetes/pkg/apis/core/v1/helper" "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/util/mount"
) )
var nodeLabels map[string]string = map[string]string{ var nodeLabels map[string]string = map[string]string{
@ -263,3 +265,42 @@ func TestZonesToSet(t *testing.T) {
} }
} }
} }
func TestDoUnmountMountPoint(t *testing.T) {
tmpDir1, err1 := utiltesting.MkTmpdir("umount_test1")
if err1 != nil {
t.Fatalf("error creating temp dir: %v", err1)
}
defer os.RemoveAll(tmpDir1)
tmpDir2, err2 := utiltesting.MkTmpdir("umount_test2")
if err2 != nil {
t.Fatalf("error creating temp dir: %v", err2)
}
defer os.RemoveAll(tmpDir2)
// Second part: want no error
tests := []struct {
mountPath string
corruptedMnt bool
}{
{
mountPath: tmpDir1,
corruptedMnt: true,
},
{
mountPath: tmpDir2,
corruptedMnt: false,
},
}
fake := &mount.FakeMounter{}
for _, tt := range tests {
err := doUnmountMountPoint(tt.mountPath, fake, false, tt.corruptedMnt)
if err != nil {
t.Errorf("err Expected nil, but got: %v", err)
}
}
}