Skip to content

Commit ce67de7

Browse files
Merge pull request kubernetes#18225 from humblec/kubelet-mount-fix
Automatic merge from submit-queue (batch tested with PRs 18225, 18351, 18331, 18340, 18326). UPSTREAM: 52324: Fix bug on kubelet failure to umount mount points. Origin-commit: f2b1e4215f6bf560f69f6aad24e6662e29ead850
2 parents 429cf24 + 661d107 commit ce67de7

File tree

2 files changed

+94
-18
lines changed

2 files changed

+94
-18
lines changed

pkg/volume/util/util.go

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"path"
2424
"path/filepath"
2525
"strings"
26+
"syscall"
2627

2728
"github.com/golang/glog"
2829
"k8s.io/api/core/v1"
@@ -96,29 +97,42 @@ func UnmountPath(mountPath string, mounter mount.Interface) error {
9697
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
9798
// IsNotMountPoint is more expensive but properly handles bind mounts.
9899
func UnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMountPointCheck bool) error {
99-
if pathExists, pathErr := PathExists(mountPath); pathErr != nil {
100-
return fmt.Errorf("Error checking if path exists: %v", pathErr)
101-
} else if !pathExists {
100+
pathExists, pathErr := PathExists(mountPath)
101+
if !pathExists {
102102
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
103103
return nil
104104
}
105-
106-
var notMnt bool
107-
var err error
108-
109-
if extensiveMountPointCheck {
110-
notMnt, err = mount.IsNotMountPoint(mounter, mountPath)
111-
} else {
112-
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
105+
corruptedMnt := isCorruptedMnt(pathErr)
106+
if pathErr != nil && !corruptedMnt {
107+
return fmt.Errorf("Error checking path: %v", pathErr)
113108
}
109+
return doUnmountMountPoint(mountPath, mounter, extensiveMountPointCheck, corruptedMnt)
110+
}
114111

115-
if err != nil {
116-
return err
117-
}
112+
// doUnmountMountPoint is a common unmount routine that unmounts the given path and
113+
// deletes the remaining directory if successful.
114+
// if extensiveMountPointCheck is true
115+
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
116+
// IsNotMountPoint is more expensive but properly handles bind mounts.
117+
// if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, Take it as an argument for convenience of testing
118+
func doUnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMountPointCheck bool, corruptedMnt bool) error {
119+
if !corruptedMnt {
120+
var notMnt bool
121+
var err error
122+
if extensiveMountPointCheck {
123+
notMnt, err = mount.IsNotMountPoint(mounter, mountPath)
124+
} else {
125+
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
126+
}
118127

119-
if notMnt {
120-
glog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
121-
return os.Remove(mountPath)
128+
if err != nil {
129+
return err
130+
}
131+
132+
if notMnt {
133+
glog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
134+
return os.Remove(mountPath)
135+
}
122136
}
123137

124138
// Unmount the mount path
@@ -128,7 +142,7 @@ func UnmountMountPoint(mountPath string, mounter mount.Interface, extensiveMount
128142
}
129143
notMnt, mntErr := mounter.IsLikelyNotMountPoint(mountPath)
130144
if mntErr != nil {
131-
return err
145+
return mntErr
132146
}
133147
if notMnt {
134148
glog.V(4).Infof("%q is unmounted, deleting the directory", mountPath)
@@ -144,11 +158,32 @@ func PathExists(path string) (bool, error) {
144158
return true, nil
145159
} else if os.IsNotExist(err) {
146160
return false, nil
161+
} else if isCorruptedMnt(err) {
162+
return true, err
147163
} else {
148164
return false, err
149165
}
150166
}
151167

168+
// isCorruptedMnt return true if err is about corrupted mount point
169+
func isCorruptedMnt(err error) bool {
170+
if err == nil {
171+
return false
172+
}
173+
var underlyingError error
174+
switch pe := err.(type) {
175+
case nil:
176+
return false
177+
case *os.PathError:
178+
underlyingError = pe.Err
179+
case *os.LinkError:
180+
underlyingError = pe.Err
181+
case *os.SyscallError:
182+
underlyingError = pe.Err
183+
}
184+
return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE
185+
}
186+
152187
// GetSecretForPod locates secret by name in the pod's namespace and returns secret map
153188
func GetSecretForPod(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (map[string]string, error) {
154189
secret := make(map[string]string)

pkg/volume/util/util_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ import (
2424
"k8s.io/api/core/v1"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
"k8s.io/apimachinery/pkg/util/sets"
27+
utiltesting "k8s.io/client-go/util/testing"
2728
// util.go uses api.Codecs.LegacyCodec so import this package to do some
2829
// resource initialization.
2930
_ "k8s.io/kubernetes/pkg/apis/core/install"
3031
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
32+
"k8s.io/kubernetes/pkg/util/mount"
3133
)
3234

3335
var nodeLabels map[string]string = map[string]string{
@@ -263,3 +265,42 @@ func TestZonesToSet(t *testing.T) {
263265
}
264266
}
265267
}
268+
269+
func TestDoUnmountMountPoint(t *testing.T) {
270+
271+
tmpDir1, err1 := utiltesting.MkTmpdir("umount_test1")
272+
if err1 != nil {
273+
t.Fatalf("error creating temp dir: %v", err1)
274+
}
275+
defer os.RemoveAll(tmpDir1)
276+
277+
tmpDir2, err2 := utiltesting.MkTmpdir("umount_test2")
278+
if err2 != nil {
279+
t.Fatalf("error creating temp dir: %v", err2)
280+
}
281+
defer os.RemoveAll(tmpDir2)
282+
283+
// Second part: want no error
284+
tests := []struct {
285+
mountPath string
286+
corruptedMnt bool
287+
}{
288+
{
289+
mountPath: tmpDir1,
290+
corruptedMnt: true,
291+
},
292+
{
293+
mountPath: tmpDir2,
294+
corruptedMnt: false,
295+
},
296+
}
297+
298+
fake := &mount.FakeMounter{}
299+
300+
for _, tt := range tests {
301+
err := doUnmountMountPoint(tt.mountPath, fake, false, tt.corruptedMnt)
302+
if err != nil {
303+
t.Errorf("err Expected nil, but got: %v", err)
304+
}
305+
}
306+
}

0 commit comments

Comments
 (0)