Refactor pkg/util/mount to be more reusable

This patch refactors pkg/util/mount to be more usable outside of
Kubernetes. This is done by refactoring mount.Interface to only contain
methods that are not K8s specific. Methods that are not relevant to
basic mount activities but still have OS-specific implementations are
now found in a mount.HostUtils interface.
This commit is contained in:
Travis Rhoden 2019-05-09 16:15:42 -06:00
parent 11abb58a5b
commit be7da5052f
47 changed files with 801 additions and 681 deletions

View File

@ -372,6 +372,7 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
mounter := mount.New(s.ExperimentalMounterPath)
subpather := subpath.New(mounter)
hu := mount.NewHostUtil()
var pluginRunner = exec.New()
if s.Containerized {
klog.V(2).Info("Running kubelet in containerized mode")
@ -382,6 +383,7 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
mounter = nsutil.NewMounter(s.RootDirectory, ne)
// NSenter only valid on Linux
subpather = subpath.NewNSEnter(mounter, ne, s.RootDirectory)
hu = nsutil.NewHostUtil(ne, s.RootDirectory)
// an exec interface which can use nsenter for flex plugin calls
pluginRunner, err = nsenter.NewNsenter(nsenter.DefaultHostRootFsPath, exec.New())
if err != nil {
@ -407,6 +409,7 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
KubeClient: nil,
HeartbeatClient: nil,
EventClient: nil,
HostUtil: hu,
Mounter: mounter,
Subpather: subpather,
OOMAdjuster: oom.NewOOMAdjuster(),

View File

@ -250,6 +250,7 @@ type Dependencies struct {
OnHeartbeatFailure func()
KubeClient clientset.Interface
Mounter mount.Interface
HostUtil mount.HostUtils
OOMAdjuster *oom.OOMAdjuster
OSInterface kubecontainer.OSInterface
PodConfig *config.PodConfig
@ -518,6 +519,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
cgroupsPerQOS: kubeCfg.CgroupsPerQOS,
cgroupRoot: kubeCfg.CgroupRoot,
mounter: kubeDeps.Mounter,
hostutil: kubeDeps.HostUtil,
subpather: kubeDeps.Subpather,
maxPods: int(kubeCfg.MaxPods),
podsPerCore: int(kubeCfg.PodsPerCore),
@ -812,6 +814,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
klet.volumePluginMgr,
klet.containerRuntime,
kubeDeps.Mounter,
kubeDeps.HostUtil,
klet.getPodsDir(),
kubeDeps.Recorder,
experimentalCheckNodeCapabilitiesBeforeMount,
@ -1096,6 +1099,9 @@ type Kubelet struct {
// Mounter to use for volumes.
mounter mount.Interface
// hostutil to interact with filesystems
hostutil mount.HostUtils
// subpather to execute subpath actions
subpather subpath.Interface
@ -1229,7 +1235,7 @@ func (kl *Kubelet) setupDataDirs() error {
if err := os.MkdirAll(kl.getRootDir(), 0750); err != nil {
return fmt.Errorf("error creating root directory: %v", err)
}
if err := kl.mounter.MakeRShared(kl.getRootDir()); err != nil {
if err := kl.hostutil.MakeRShared(kl.getRootDir()); err != nil {
return fmt.Errorf("error configuring root directory: %v", err)
}
if err := os.MkdirAll(kl.getPodsDir(), 0750); err != nil {

View File

@ -128,7 +128,7 @@ func (kl *Kubelet) makeBlockVolumes(pod *v1.Pod, container *v1.Container, podVol
}
// makeMounts determines the mount points for the given container.
func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap, mounter mountutil.Interface, subpather subpath.Interface, expandEnvs []kubecontainer.EnvVar) ([]kubecontainer.Mount, func(), error) {
func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap, hu mountutil.HostUtils, subpather subpath.Interface, expandEnvs []kubecontainer.EnvVar) ([]kubecontainer.Mount, func(), error) {
// Kubernetes only mounts on /etc/hosts if:
// - container is not an infrastructure (pause) container
// - container is not already mounting on /etc/hosts
@ -195,7 +195,7 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
volumePath := hostPath
hostPath = filepath.Join(volumePath, subPath)
if subPathExists, err := mounter.ExistsPath(hostPath); err != nil {
if subPathExists, err := hu.ExistsPath(hostPath); err != nil {
klog.Errorf("Could not determine if subPath %s exists; will not attempt to change its permissions", hostPath)
} else if !subPathExists {
// Create the sub path now because if it's auto-created later when referenced, it may have an
@ -203,7 +203,7 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
// when the pod specifies an fsGroup, and if the directory is not created here, Docker will
// later auto-create it with the incorrect mode 0750
// Make extra care not to escape the volume!
perm, err := mounter.GetMode(volumePath)
perm, err := hu.GetMode(volumePath)
if err != nil {
return nil, cleanupAction, err
}
@ -465,7 +465,7 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *v1.Pod, container *v1.Contai
}
opts.Envs = append(opts.Envs, envs...)
mounts, cleanupAction, err := makeMounts(pod, kl.getPodDir(pod.UID), container, hostname, hostDomainName, podIP, volumes, kl.mounter, kl.subpather, opts.Envs)
mounts, cleanupAction, err := makeMounts(pod, kl.getPodDir(pod.UID), container, hostname, hostDomainName, podIP, volumes, kl.hostutil, kl.subpather, opts.Envs)
if err != nil {
return nil, cleanupAction, err
}

View File

@ -241,7 +241,7 @@ func TestMakeMounts(t *testing.T) {
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
fm := &mount.FakeMounter{}
fhu := &mount.FakeHostUtil{}
fsp := &subpath.FakeSubpath{}
pod := v1.Pod{
Spec: v1.PodSpec{
@ -249,7 +249,7 @@ func TestMakeMounts(t *testing.T) {
},
}
mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes, fm, fsp, nil)
mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes, fhu, fsp, nil)
// validate only the error if we expect an error
if tc.expectErr {

View File

@ -53,7 +53,7 @@ import (
)
func TestDisabledSubpath(t *testing.T) {
fm := &mount.FakeMounter{}
fhu := &mount.FakeHostUtil{}
fsp := &subpath.FakeSubpath{}
pod := v1.Pod{
Spec: v1.PodSpec{
@ -97,7 +97,7 @@ func TestDisabledSubpath(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)()
for name, test := range cases {
_, _, err := makeMounts(&pod, "/pod", &test.container, "fakepodname", "", "", podVolumes, fm, fsp, nil)
_, _, err := makeMounts(&pod, "/pod", &test.container, "fakepodname", "", "", podVolumes, fhu, fsp, nil)
if err != nil && !test.expectError {
t.Errorf("test %v failed: %v", name, err)
}

View File

@ -82,9 +82,9 @@ func TestMakeMountsWindows(t *testing.T) {
},
}
fm := &mount.FakeMounter{}
fhu := &mount.FakeHostUtil{}
fsp := &subpath.FakeSubpath{}
mounts, _, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", "", podVolumes, fm, fsp, nil)
mounts, _, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", "", podVolumes, fhu, fsp, nil)
expectedMounts := []kubecontainer.Mount{
{

View File

@ -162,6 +162,7 @@ func newTestKubeletWithImageList(
kubelet.heartbeatClient = fakeKubeClient
kubelet.os = &containertest.FakeOS{}
kubelet.mounter = &mount.FakeMounter{}
kubelet.hostutil = &mount.FakeHostUtil{}
kubelet.subpather = &subpath.FakeSubpath{}
kubelet.hostname = testKubeletHostname
@ -312,7 +313,6 @@ func newTestKubeletWithImageList(
NewInitializedVolumePluginMgr(kubelet, kubelet.secretManager, kubelet.configMapManager, token.NewManager(kubelet.kubeClient), allPlugins, prober)
require.NoError(t, err, "Failed to initialize VolumePluginMgr")
kubelet.mounter = &mount.FakeMounter{}
kubelet.volumeManager = kubeletvolume.NewVolumeManager(
controllerAttachDetachEnabled,
kubelet.nodeName,
@ -322,6 +322,7 @@ func newTestKubeletWithImageList(
kubelet.volumePluginMgr,
fakeRuntime,
kubelet.mounter,
kubelet.hostutil,
kubelet.getPodsDir(),
kubelet.recorder,
false, /* experimentalCheckNodeCapabilitiesBeforeMount*/

View File

@ -85,6 +85,7 @@ func TestRunOnce(t *testing.T) {
hostname: testKubeletHostname,
nodeName: testKubeletHostname,
runtimeState: newRuntimeState(time.Second),
hostutil: &mount.FakeHostUtil{},
}
kb.containerManager = cm.NewStubContainerManager()
@ -103,6 +104,7 @@ func TestRunOnce(t *testing.T) {
kb.volumePluginMgr,
fakeRuntime,
kb.mounter,
kb.hostutil,
kb.getPodsDir(),
kb.recorder,
false, /* experimentalCheckNodeCapabilitiesBeforeMount */

View File

@ -160,6 +160,10 @@ func (kvh *kubeletVolumeHost) GetSubpather() subpath.Interface {
return kvh.kubelet.subpather
}
func (kvh *kubeletVolumeHost) GetHostUtil() mount.HostUtils {
return kvh.kubelet.hostutil
}
func (kvh *kubeletVolumeHost) GetInformerFactory() informers.SharedInformerFactory {
return kvh.informerFactory
}

View File

@ -87,6 +87,7 @@ type Reconciler interface {
// safely (prevents more than one operation from being triggered on the same
// volume)
// mounter - mounter passed in from kubelet, passed down unmount path
// hostutil - hostutil passed in from kubelet
// volumePluginMgr - volume plugin manager passed from kubelet
func NewReconciler(
kubeClient clientset.Interface,
@ -99,6 +100,7 @@ func NewReconciler(
populatorHasAddedPods func() bool,
operationExecutor operationexecutor.OperationExecutor,
mounter mount.Interface,
hostutil mount.HostUtils,
volumePluginMgr *volumepkg.VolumePluginMgr,
kubeletPodsDir string) Reconciler {
return &reconciler{
@ -112,6 +114,7 @@ func NewReconciler(
populatorHasAddedPods: populatorHasAddedPods,
operationExecutor: operationExecutor,
mounter: mounter,
hostutil: hostutil,
volumePluginMgr: volumePluginMgr,
kubeletPodsDir: kubeletPodsDir,
timeOfLastSync: time.Time{},
@ -130,6 +133,7 @@ type reconciler struct {
populatorHasAddedPods func() bool
operationExecutor operationexecutor.OperationExecutor
mounter mount.Interface
hostutil mount.HostUtils
volumePluginMgr *volumepkg.VolumePluginMgr
kubeletPodsDir string
timeOfLastSync time.Time
@ -278,7 +282,7 @@ func (rc *reconciler) reconcile() {
// Volume is globally mounted to device, unmount it
klog.V(5).Infof(attachedVolume.GenerateMsgDetailed("Starting operationExecutor.UnmountDevice", ""))
err := rc.operationExecutor.UnmountDevice(
attachedVolume.AttachedVolume, rc.actualStateOfWorld, rc.mounter)
attachedVolume.AttachedVolume, rc.actualStateOfWorld, rc.hostutil)
if err != nil &&
!nestedpendingoperations.IsAlreadyExists(err) &&
!exponentialbackoff.IsExponentialBackoff(err) {

View File

@ -84,6 +84,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
@ -127,6 +128,7 @@ func Test_Run_Positive_VolumeAttachAndMount(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -204,6 +206,7 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -282,6 +285,7 @@ func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -371,6 +375,7 @@ func Test_Run_Positive_VolumeUnmountControllerAttachEnabled(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -461,6 +466,7 @@ func Test_Run_Positive_VolumeAttachAndMap(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -545,6 +551,7 @@ func Test_Run_Positive_BlockVolumeMapControllerAttachEnabled(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -630,6 +637,7 @@ func Test_Run_Positive_BlockVolumeAttachMapUnmapDetach(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -725,6 +733,7 @@ func Test_Run_Positive_VolumeUnmapControllerAttachEnabled(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{
@ -918,11 +927,11 @@ func Test_GenerateUnmapDeviceFunc_Plugin_Not_Found(t *testing.T) {
nil, /* fakeRecorder */
false, /* checkNodeCapabilitiesBeforeMount */
nil))
var mounter mount.Interface
var hostutil mount.HostUtils
volumeMode := v1.PersistentVolumeBlock
tmpSpec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{VolumeMode: &volumeMode}}}
deviceToDetach := operationexecutor.AttachedVolume{VolumeSpec: tmpSpec, PluginName: "fake-file-plugin"}
err := oex.UnmountDevice(deviceToDetach, asw, mounter)
err := oex.UnmountDevice(deviceToDetach, asw, hostutil)
// Assert
if assert.Error(t, err) {
assert.Contains(t, err.Error(), tc.expectedErrMsg)
@ -1004,6 +1013,7 @@ func Test_Run_Positive_VolumeFSResizeControllerAttachEnabled(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
@ -1181,6 +1191,7 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabledRace(t *testing.T) {
hasAddedPods,
oex,
&mount.FakeMounter{},
&mount.FakeHostUtil{},
volumePluginMgr,
kubeletPodsDir)
pod := &v1.Pod{

View File

@ -151,6 +151,7 @@ func NewVolumeManager(
volumePluginMgr *volume.VolumePluginMgr,
kubeContainerRuntime container.Runtime,
mounter mount.Interface,
hostutil mount.HostUtils,
kubeletPodsDir string,
recorder record.EventRecorder,
checkNodeCapabilitiesBeforeMount bool,
@ -190,6 +191,7 @@ func NewVolumeManager(
vm.desiredStateOfWorldPopulator.HasAddedPods,
vm.operationExecutor,
mounter,
hostutil,
volumePluginMgr,
kubeletPodsDir)

View File

@ -232,6 +232,7 @@ func newTestVolumeManager(tmpDir string, podManager kubepod.Manager, kubeClient
plugMgr,
&containertest.FakeRuntime{},
&mount.FakeMounter{},
&mount.FakeHostUtil{},
"",
fakeRecorder,
false, /* experimentalCheckNodeCapabilitiesBeforeMount */

View File

@ -80,6 +80,7 @@ func NewHollowKubelet(
OOMAdjuster: oom.NewFakeOOMAdjuster(),
Mounter: mount.New("" /* default mount path */),
Subpather: &subpath.FakeSubpath{},
HostUtil: &mount.FakeHostUtil{},
}
return &HollowKubelet{

View File

@ -29,7 +29,6 @@ import (
type FakeMounter struct {
MountPoints []MountPoint
Log []FakeAction
Filesystem map[string]FileType
// Error to return for a path when calling IsLikelyNotMountPoint
MountCheckErrors map[string]error
// Some tests run things in parallel, make sure the mounter does not produce
@ -166,56 +165,6 @@ func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil
}
func (f *FakeMounter) DeviceOpened(pathname string) (bool, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
for _, mp := range f.MountPoints {
if mp.Device == pathname {
return true, nil
}
}
return false, nil
}
func (f *FakeMounter) PathIsDevice(pathname string) (bool, error) {
return true, nil
}
func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return getDeviceNameFromMount(f, mountPath, pluginMountDir)
}
func (f *FakeMounter) MakeRShared(path string) error {
return nil
}
func (f *FakeMounter) GetFileType(pathname string) (FileType, error) {
if t, ok := f.Filesystem[pathname]; ok {
return t, nil
}
return FileType("Directory"), nil
}
func (f *FakeMounter) MakeDir(pathname string) error {
return nil
}
func (f *FakeMounter) MakeFile(pathname string) error {
return nil
}
func (f *FakeMounter) ExistsPath(pathname string) (bool, error) {
if _, ok := f.Filesystem[pathname]; ok {
return true, nil
}
return false, nil
}
func (f *FakeMounter) EvalHostSymlinks(pathname string) (string, error) {
return pathname, nil
}
func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
realpath, err := filepath.EvalSymlinks(pathname)
if err != nil {
@ -225,14 +174,73 @@ func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
return getMountRefsByDev(f, realpath)
}
func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) {
type FakeHostUtil struct {
MountPoints []MountPoint
Filesystem map[string]FileType
mutex sync.Mutex
}
var _ HostUtils = &FakeHostUtil{}
func (hu *FakeHostUtil) DeviceOpened(pathname string) (bool, error) {
hu.mutex.Lock()
defer hu.mutex.Unlock()
for _, mp := range hu.MountPoints {
if mp.Device == pathname {
return true, nil
}
}
return false, nil
}
func (hu *FakeHostUtil) PathIsDevice(pathname string) (bool, error) {
return true, nil
}
func (hu *FakeHostUtil) GetDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
}
func (hu *FakeHostUtil) MakeRShared(path string) error {
return nil
}
func (hu *FakeHostUtil) GetFileType(pathname string) (FileType, error) {
if t, ok := hu.Filesystem[pathname]; ok {
return t, nil
}
return FileType("Directory"), nil
}
func (hu *FakeHostUtil) MakeDir(pathname string) error {
return nil
}
func (hu *FakeHostUtil) MakeFile(pathname string) error {
return nil
}
func (hu *FakeHostUtil) ExistsPath(pathname string) (bool, error) {
if _, ok := hu.Filesystem[pathname]; ok {
return true, nil
}
return false, nil
}
func (hu *FakeHostUtil) EvalHostSymlinks(pathname string) (string, error) {
return pathname, nil
}
func (hu *FakeHostUtil) GetFSGroup(pathname string) (int64, error) {
return -1, errors.New("GetFSGroup not implemented")
}
func (f *FakeMounter) GetSELinuxSupport(pathname string) (bool, error) {
func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return false, errors.New("GetSELinuxSupport not implemented")
}
func (f *FakeMounter) GetMode(pathname string) (os.FileMode, error) {
func (hu *FakeHostUtil) GetMode(pathname string) (os.FileMode, error) {
return 0, errors.New("not implemented")
}

View File

@ -55,6 +55,13 @@ type Interface interface {
// IsLikelyNotMountPoint does NOT properly detect all mountpoint types
// most notably linux bind mounts and symbolic link.
IsLikelyNotMountPoint(file string) (bool, error)
// GetMountRefs finds all mount references to the path, returns a
// list of paths. Path could be a mountpoint path, device or a normal
// directory (for bind mount).
GetMountRefs(pathname string) ([]string, error)
}
type HostUtils interface {
// DeviceOpened determines if the device is in use elsewhere
// on the system, i.e. still mounted.
DeviceOpened(pathname string) (bool, error)
@ -62,7 +69,7 @@ type Interface interface {
PathIsDevice(pathname string) (bool, error)
// GetDeviceNameFromMount finds the device name by checking the mount path
// to get the global mount path within its plugin directory
GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error)
GetDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error)
// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. If not, it bind-mounts the path as rshared.
MakeRShared(path string) error
@ -81,10 +88,6 @@ type Interface interface {
// EvalHostSymlinks returns the path name after evaluating symlinks.
// Will operate in the host mount namespace if kubelet is running in a container.
EvalHostSymlinks(pathname string) (string, error)
// GetMountRefs finds all mount references to the path, returns a
// list of paths. Path could be a mountpoint path, device or a normal
// directory (for bind mount).
GetMountRefs(pathname string) ([]string, error)
// GetFSGroup returns FSGroup of the path.
GetFSGroup(pathname string) (int64, error)
// GetSELinuxSupport returns true if given path is on a mount that supports
@ -123,6 +126,10 @@ type Exec interface {
// the mount interface
var _ Interface = &Mounter{}
// Compile-time check to ensure all HostUtil implementations satisfy
// the HostUtils Interface
var _ HostUtils = &hostUtil{}
// This represents a single line in /proc/mounts or /etc/fstab.
type MountPoint struct {
Device string
@ -239,7 +246,8 @@ func IsNotMountPoint(mounter Interface, file string) (bool, error) {
}
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts
resolvedFile, err := mounter.EvalHostSymlinks(file)
hu := NewHostUtil()
resolvedFile, err := hu.EvalHostSymlinks(file)
if err != nil {
return true, err
}

View File

@ -21,11 +21,17 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"syscall"
"testing"
)
func TestDoCleanupMountPoint(t *testing.T) {
if runtime.GOOS == "darwin" {
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
}
const testMount = "test-mount"
const defaultPerm = 0750

View File

@ -249,179 +249,21 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil
}
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// If pathname is not a device, log and return false with nil error.
// If open returns errno EBUSY, return true with nil error.
// If open returns nil, return false with nil error.
// Otherwise, return false with error
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
return ExclusiveOpenFailsOnDevice(pathname)
}
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
pathType, err := mounter.GetFileType(pathname)
isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
return isDevice, err
}
// ExclusiveOpenFailsOnDevice is shared with NsEnterMounter
func ExclusiveOpenFailsOnDevice(pathname string) (bool, error) {
var isDevice bool
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
isDevice = false
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
pathExists, pathErr := PathExists(pathname)
if !pathExists {
return []string{}, nil
} else if IsCorruptedMnt(pathErr) {
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
return []string{}, nil
} else if pathErr != nil {
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
}
// err in call to os.Stat
if err != nil {
return false, fmt.Errorf(
"PathIsDevice failed for path %q: %v",
pathname,
err)
}
// path refers to a device
if finfo.Mode()&os.ModeDevice != 0 {
isDevice = true
}
if !isDevice {
klog.Errorf("Path %q is not referring to a device.", pathname)
return false, nil
}
fd, errno := unix.Open(pathname, unix.O_RDONLY|unix.O_EXCL, 0)
// If the device is in use, open will return an invalid fd.
// When this happens, it is expected that Close will fail and throw an error.
defer unix.Close(fd)
if errno == nil {
// device not in use
return false, nil
} else if errno == unix.EBUSY {
// device is in use
return true, nil
}
// error during call to Open
return false, errno
}
//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
}
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
}
// GetDeviceNameFromMountLinux find the device name from /proc/mounts in which
// the mount path reference should match the given plugin mount directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
// This implementation is shared with NsEnterMounter
func GetDeviceNameFromMountLinux(mounter Interface, mountPath, pluginMountDir string) (string, error) {
refs, err := mounter.GetMountRefs(mountPath)
if err != nil {
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
}
if len(refs) == 0 {
klog.V(4).Infof("Directory %s is not mounted", mountPath)
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
for _, ref := range refs {
if strings.HasPrefix(ref, pluginMountDir) {
volumeID, err := filepath.Rel(pluginMountDir, ref)
if err != nil {
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
// ListProcMounts is shared with NsEnterMounter
func ListProcMounts(mountFilePath string) ([]MountPoint, error) {
content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
realpath, err := filepath.EvalSymlinks(pathname)
if err != nil {
return nil, err
}
return parseProcMounts(content)
}
func parseProcMounts(content []byte) ([]MountPoint, error) {
out := []MountPoint{}
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if line == "" {
// the last split() item is empty string following the last \n
continue
}
fields := strings.Fields(line)
if len(fields) != expectedNumFieldsPerLine {
return nil, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
}
mp := MountPoint{
Device: fields[0],
Path: fields[1],
Type: fields[2],
Opts: strings.Split(fields[3], ","),
}
freq, err := strconv.Atoi(fields[4])
if err != nil {
return nil, err
}
mp.Freq = freq
pass, err := strconv.Atoi(fields[5])
if err != nil {
return nil, err
}
mp.Pass = pass
out = append(out, mp)
}
return out, nil
}
func (mounter *Mounter) MakeRShared(path string) error {
return DoMakeRShared(path, procMountInfoPath)
}
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
return getFileType(pathname)
}
func (mounter *Mounter) MakeDir(pathname string) error {
err := os.MkdirAll(pathname, os.FileMode(0755))
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
func (mounter *Mounter) MakeFile(pathname string) error {
f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
defer f.Close()
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
}
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
return filepath.EvalSymlinks(pathname)
return SearchMountPoints(realpath, procMountInfoPath)
}
// formatAndMount uses unix utils to format and mount the given disk
@ -562,6 +404,188 @@ func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
return fstype, nil
}
// ListProcMounts is shared with NsEnterMounter
func ListProcMounts(mountFilePath string) ([]MountPoint, error) {
content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
if err != nil {
return nil, err
}
return parseProcMounts(content)
}
func parseProcMounts(content []byte) ([]MountPoint, error) {
out := []MountPoint{}
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if line == "" {
// the last split() item is empty string following the last \n
continue
}
fields := strings.Fields(line)
if len(fields) != expectedNumFieldsPerLine {
return nil, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
}
mp := MountPoint{
Device: fields[0],
Path: fields[1],
Type: fields[2],
Opts: strings.Split(fields[3], ","),
}
freq, err := strconv.Atoi(fields[4])
if err != nil {
return nil, err
}
mp.Freq = freq
pass, err := strconv.Atoi(fields[5])
if err != nil {
return nil, err
}
mp.Pass = pass
out = append(out, mp)
}
return out, nil
}
type hostUtil struct {
}
func NewHostUtil() HostUtils {
return &hostUtil{}
}
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// If pathname is not a device, log and return false with nil error.
// If open returns errno EBUSY, return true with nil error.
// If open returns nil, return false with nil error.
// Otherwise, return false with error
func (hu *hostUtil) DeviceOpened(pathname string) (bool, error) {
return ExclusiveOpenFailsOnDevice(pathname)
}
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (hu *hostUtil) PathIsDevice(pathname string) (bool, error) {
pathType, err := hu.GetFileType(pathname)
isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
return isDevice, err
}
// ExclusiveOpenFailsOnDevice is shared with NsEnterMounter
func ExclusiveOpenFailsOnDevice(pathname string) (bool, error) {
var isDevice bool
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
isDevice = false
}
// err in call to os.Stat
if err != nil {
return false, fmt.Errorf(
"PathIsDevice failed for path %q: %v",
pathname,
err)
}
// path refers to a device
if finfo.Mode()&os.ModeDevice != 0 {
isDevice = true
}
if !isDevice {
klog.Errorf("Path %q is not referring to a device.", pathname)
return false, nil
}
fd, errno := unix.Open(pathname, unix.O_RDONLY|unix.O_EXCL, 0)
// If the device is in use, open will return an invalid fd.
// When this happens, it is expected that Close will fail and throw an error.
defer unix.Close(fd)
if errno == nil {
// device not in use
return false, nil
} else if errno == unix.EBUSY {
// device is in use
return true, nil
}
// error during call to Open
return false, errno
}
//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
func (hu *hostUtil) GetDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
}
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
}
// GetDeviceNameFromMountLinux find the device name from /proc/mounts in which
// the mount path reference should match the given plugin mount directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
// This implementation is shared with NsEnterMounter
func GetDeviceNameFromMountLinux(mounter Interface, mountPath, pluginMountDir string) (string, error) {
refs, err := mounter.GetMountRefs(mountPath)
if err != nil {
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
}
if len(refs) == 0 {
klog.V(4).Infof("Directory %s is not mounted", mountPath)
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
for _, ref := range refs {
if strings.HasPrefix(ref, pluginMountDir) {
volumeID, err := filepath.Rel(pluginMountDir, ref)
if err != nil {
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
func (hu *hostUtil) MakeRShared(path string) error {
return DoMakeRShared(path, procMountInfoPath)
}
func (hu *hostUtil) GetFileType(pathname string) (FileType, error) {
return getFileType(pathname)
}
func (hu *hostUtil) MakeDir(pathname string) error {
err := os.MkdirAll(pathname, os.FileMode(0755))
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
func (hu *hostUtil) MakeFile(pathname string) error {
f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
defer f.Close()
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
func (hu *hostUtil) ExistsPath(pathname string) (bool, error) {
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
}
func (hu *hostUtil) EvalHostSymlinks(pathname string) (string, error) {
return filepath.EvalSymlinks(pathname)
}
// isShared returns true, if given path is on a mount point that has shared
// mount propagation.
func isShared(mount string, mountInfoPath string) (bool, error) {
@ -726,28 +750,11 @@ func GetSELinux(path string, mountInfoFilename string) (bool, error) {
return false, nil
}
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
pathExists, pathErr := PathExists(pathname)
if !pathExists {
return []string{}, nil
} else if IsCorruptedMnt(pathErr) {
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
return []string{}, nil
} else if pathErr != nil {
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
}
realpath, err := filepath.EvalSymlinks(pathname)
if err != nil {
return nil, err
}
return SearchMountPoints(realpath, procMountInfoPath)
}
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
func (hu *hostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return GetSELinux(pathname, procMountInfoPath)
}
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
func (hu *hostUtil) GetFSGroup(pathname string) (int64, error) {
realpath, err := filepath.EvalSymlinks(pathname)
if err != nil {
return 0, err
@ -755,7 +762,7 @@ func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
return GetFSGroupLinux(realpath)
}
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
func (hu *hostUtil) GetMode(pathname string) (os.FileMode, error) {
return GetModeLinux(pathname)
}

View File

@ -661,7 +661,7 @@ func createSocketFile(socketDir string) (string, error) {
}
func TestGetFileType(t *testing.T) {
mounter := Mounter{"fake/path", false}
hu := NewHostUtil()
testCase := []struct {
name string
@ -750,7 +750,7 @@ func TestGetFileType(t *testing.T) {
defer os.RemoveAll(cleanUpPath)
}
fileType, err := mounter.GetFileType(path)
fileType, err := hu.GetFileType(path)
if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
}

View File

@ -58,24 +58,8 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, unsupportedErr
}
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return "", unsupportedErr
}
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return "", unsupportedErr
}
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
return false, unsupportedErr
}
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
return true, unsupportedErr
}
func (mounter *Mounter) MakeRShared(path string) error {
return unsupportedErr
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errors.New("not implemented")
}
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
@ -86,38 +70,65 @@ func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, erro
return true, unsupportedErr
}
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
return FileType("fake"), unsupportedErr
}
func (mounter *Mounter) MakeDir(pathname string) error {
return unsupportedErr
}
func (mounter *Mounter) MakeFile(pathname string) error {
return unsupportedErr
}
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
return true, errors.New("not implemented")
}
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return "", unsupportedErr
}
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errors.New("not implemented")
type hostUtil struct{}
func NewHostUtil() HostUtils {
return &hostUtil{}
}
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
return -1, errors.New("not implemented")
// DeviceOpened determines if the device is in use elsewhere
func (hu *hostUtil) DeviceOpened(pathname string) (bool, error) {
return false, unsupportedErr
}
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
return false, errors.New("not implemented")
// PathIsDevice determines if a path is a device.
func (hu *hostUtil) PathIsDevice(pathname string) (bool, error) {
return true, unsupportedErr
}
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
return 0, errors.New("not implemented")
// GetDeviceNameFromMount finds the device name by checking the mount path
// to get the global mount path within its plugin directory
func (hu *hostUtil) GetDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return "", unsupportedErr
}
func (hu *hostUtil) MakeRShared(path string) error {
return unsupportedErr
}
func (hu *hostUtil) GetFileType(pathname string) (FileType, error) {
return FileType("fake"), unsupportedErr
}
func (hu *hostUtil) MakeFile(pathname string) error {
return unsupportedErr
}
func (hu *hostUtil) MakeDir(pathname string) error {
return unsupportedErr
}
func (hu *hostUtil) ExistsPath(pathname string) (bool, error) {
return true, unsupportedErr
}
// EvalHostSymlinks returns the path name after evaluating symlinks
func (hu *hostUtil) EvalHostSymlinks(pathname string) (string, error) {
return "", unsupportedErr
}
func (hu *hostUtil) GetFSGroup(pathname string) (int64, error) {
return -1, unsupportedErr
}
func (hu *hostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return false, unsupportedErr
}
func (hu *hostUtil) GetMode(pathname string) (os.FileMode, error) {
return 0, unsupportedErr
}

View File

@ -185,7 +185,8 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
if err != nil {
return true, fmt.Errorf("readlink error: %v", err)
}
exists, err := mounter.ExistsPath(target)
hu := NewHostUtil()
exists, err := hu.ExistsPath(target)
if err != nil {
return true, err
}
@ -195,90 +196,19 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil
}
// GetDeviceNameFromMount given a mnt point, find the device
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
}
// getDeviceNameFromMount find the device(drive) name in which
// the mount path reference should match the given plugin mount directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
refs, err := mounter.GetMountRefs(mountPath)
if err != nil {
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
windowsPath := normalizeWindowsPath(pathname)
pathExists, pathErr := PathExists(windowsPath)
if !pathExists {
return []string{}, nil
} else if IsCorruptedMnt(pathErr) {
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
return []string{}, nil
} else if pathErr != nil {
return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
}
if len(refs) == 0 {
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
basemountPath := normalizeWindowsPath(pluginMountDir)
for _, ref := range refs {
if strings.Contains(ref, basemountPath) {
volumeID, err := filepath.Rel(normalizeWindowsPath(basemountPath), ref)
if err != nil {
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
// DeviceOpened determines if the device is in use elsewhere
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
// PathIsDevice determines if a path is a device.
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
return false, nil
}
// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. Empty implementation here.
func (mounter *Mounter) MakeRShared(path string) error {
return nil
}
// GetFileType checks for sockets/block/character devices
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
return getFileType(pathname)
}
// MakeFile creates a new directory
func (mounter *Mounter) MakeDir(pathname string) error {
err := os.MkdirAll(pathname, os.FileMode(0755))
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
// MakeFile creates an empty file
func (mounter *Mounter) MakeFile(pathname string) error {
f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
defer f.Close()
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
// ExistsPath checks whether the path exists
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
}
// EvalHostSymlinks returns the path name after evaluating symlinks
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
return filepath.EvalSymlinks(pathname)
return []string{pathname}, nil
}
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
@ -379,33 +309,110 @@ func getAllParentLinks(path string) ([]string, error) {
return links, nil
}
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
windowsPath := normalizeWindowsPath(pathname)
pathExists, pathErr := PathExists(windowsPath)
if !pathExists {
return []string{}, nil
} else if IsCorruptedMnt(pathErr) {
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
return []string{}, nil
} else if pathErr != nil {
return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
type hostUtil struct{}
func NewHostUtil() HostUtils {
return &hostUtil{}
}
// GetDeviceNameFromMount given a mnt point, find the device
func (hu *hostUtil) GetDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
}
// getDeviceNameFromMount find the device(drive) name in which
// the mount path reference should match the given plugin mount directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
refs, err := mounter.GetMountRefs(mountPath)
if err != nil {
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
}
return []string{pathname}, nil
if len(refs) == 0 {
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
basemountPath := normalizeWindowsPath(pluginMountDir)
for _, ref := range refs {
if strings.Contains(ref, basemountPath) {
volumeID, err := filepath.Rel(normalizeWindowsPath(basemountPath), ref)
if err != nil {
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
// DeviceOpened determines if the device is in use elsewhere
func (hu *hostUtil) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
// PathIsDevice determines if a path is a device.
func (hu *hostUtil) PathIsDevice(pathname string) (bool, error) {
return false, nil
}
// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. Empty implementation here.
func (hu *hostUtil) MakeRShared(path string) error {
return nil
}
// GetFileType checks for sockets/block/character devices
func (hu *(hostUtil)) GetFileType(pathname string) (FileType, error) {
return getFileType(pathname)
}
// MakeFile creates a new directory
func (hu *hostUtil) MakeDir(pathname string) error {
err := os.MkdirAll(pathname, os.FileMode(0755))
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
// MakeFile creates an empty file
func (hu *hostUtil) MakeFile(pathname string) error {
f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
defer f.Close()
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
// ExistsPath checks whether the path exists
func (hu *hostUtil) ExistsPath(pathname string) (bool, error) {
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
}
// EvalHostSymlinks returns the path name after evaluating symlinks
func (hu *hostUtil) EvalHostSymlinks(pathname string) (string, error) {
return filepath.EvalSymlinks(pathname)
}
// Note that on windows, it always returns 0. We actually don't set FSGroup on
// windows platform, see SetVolumeOwnership implementation.
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
func (hu *hostUtil) GetFSGroup(pathname string) (int64, error) {
return 0, nil
}
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
func (hu *hostUtil) GetSELinuxSupport(pathname string) (bool, error) {
// Windows does not support SELinux.
return false, nil
}
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
func (hu *hostUtil) GetMode(pathname string) (os.FileMode, error) {
info, err := os.Stat(pathname)
if err != nil {
return 0, err

View File

@ -175,7 +175,7 @@ func TestPathWithinBase(t *testing.T) {
}
func TestGetFileType(t *testing.T) {
mounter := New("fake/path")
hu := NewHostUtil()
testCase := []struct {
name string
@ -213,7 +213,7 @@ func TestGetFileType(t *testing.T) {
defer os.RemoveAll(cleanUpPath)
}
fileType, err := mounter.GetFileType(path)
fileType, err := hu.GetFileType(path)
if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
}

View File

@ -250,8 +250,13 @@ func getVolumeSource(
func (plugin *awsElasticBlockStorePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
volumeID, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
volumeID, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil {
return nil, err
}

View File

@ -309,8 +309,13 @@ var _ volume.NodeExpandableVolumePlugin = &azureDataDiskPlugin{}
func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil {
return nil, err

View File

@ -267,8 +267,13 @@ func (plugin *cinderPlugin) getCloudProvider() (BlockStorageProvider, error) {
func (plugin *cinderPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil {
return nil, err
}

View File

@ -69,6 +69,7 @@ type csiMountMgr struct {
podUID types.UID
options volume.VolumeOptions
publishContext map[string]string
kubeVolHost volume.KubeletVolumeHost
volume.MetricsProvider
}
@ -328,9 +329,9 @@ func (c *csiMountMgr) podAttributes() (map[string]string, error) {
}
func (c *csiMountMgr) GetAttributes() volume.Attributes {
mounter := c.plugin.host.GetMounter(c.plugin.GetPluginName())
path := c.GetPath()
supportSelinux, err := mounter.GetSELinuxSupport(path)
hu := c.kubeVolHost.GetHostUtil()
supportSelinux, err := hu.GetSELinuxSupport(path)
if err != nil {
klog.V(2).Info(log("error checking for SELinux support: %s", err))
// Best guess

View File

@ -391,6 +391,12 @@ func (p *csiPlugin) NewMounter(
return nil, errors.New("failed to get a Kubernetes client")
}
kvh, ok := p.host.(volume.KubeletVolumeHost)
if !ok {
klog.Error(log("cast from VolumeHost to KubeletVolumeHost failed"))
return nil, errors.New("cast from VolumeHost to KubeletVolumeHost failed")
}
mounter := &csiMountMgr{
plugin: p,
k8s: k8s,
@ -402,6 +408,7 @@ func (p *csiPlugin) NewMounter(
volumeID: volumeHandle,
specVolumeID: spec.Name(),
readOnly: readOnly,
kubeVolHost: kvh,
}
mounter.csiClientGetter.driverName = csiDriverName(driverName)
@ -447,10 +454,17 @@ func (p *csiPlugin) NewMounter(
func (p *csiPlugin) NewUnmounter(specName string, podUID types.UID) (volume.Unmounter, error) {
klog.V(4).Infof(log("setting up unmounter for [name=%v, podUID=%v]", specName, podUID))
kvh, ok := p.host.(volume.KubeletVolumeHost)
if !ok {
klog.Error(log("cast from VolumeHost to KubeletVolumeHost failed"))
return nil, errors.New("cast from VolumeHost to KubeletVolumeHost failed")
}
unmounter := &csiMountMgr{
plugin: p,
podUID: podUID,
specVolumeID: specName,
kubeVolHost: kvh,
}
// load volume info from file

View File

@ -288,8 +288,13 @@ var _ volume.NodeExpandableVolumePlugin = &gcePersistentDiskPlugin{}
func (plugin *gcePersistentDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil {
return nil, err
}

View File

@ -118,10 +118,15 @@ func (plugin *hostPathPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts vo
} else {
pathType = hostPathVolumeSource.Type
}
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
return &hostPathMounter{
hostPath: &hostPath{path: path, pathType: pathType},
readOnly: readOnly,
mounter: plugin.host.GetMounter(plugin.GetPluginName()),
hu: kvh.GetHostUtil(),
}, nil
}
@ -202,6 +207,7 @@ type hostPathMounter struct {
*hostPath
readOnly bool
mounter mount.Interface
hu mount.HostUtils
}
var _ volume.Mounter = &hostPathMounter{}
@ -231,7 +237,7 @@ func (b *hostPathMounter) SetUp(mounterArgs volume.MounterArgs) error {
if *b.pathType == v1.HostPathUnset {
return nil
}
return checkType(b.GetPath(), b.pathType, b.mounter)
return checkType(b.GetPath(), b.pathType, b.hu)
}
// SetUpAt does not make sense for host paths - probably programmer error.
@ -352,13 +358,13 @@ type hostPathTypeChecker interface {
}
type fileTypeChecker struct {
path string
exists bool
mounter mount.Interface
path string
exists bool
hu mount.HostUtils
}
func (ftc *fileTypeChecker) Exists() bool {
exists, err := ftc.mounter.ExistsPath(ftc.path)
exists, err := ftc.hu.ExistsPath(ftc.path)
return exists && err == nil
}
@ -370,14 +376,14 @@ func (ftc *fileTypeChecker) IsFile() bool {
}
func (ftc *fileTypeChecker) MakeFile() error {
return ftc.mounter.MakeFile(ftc.path)
return ftc.hu.MakeFile(ftc.path)
}
func (ftc *fileTypeChecker) IsDir() bool {
if !ftc.Exists() {
return false
}
pathType, err := ftc.mounter.GetFileType(ftc.path)
pathType, err := ftc.hu.GetFileType(ftc.path)
if err != nil {
return false
}
@ -385,11 +391,11 @@ func (ftc *fileTypeChecker) IsDir() bool {
}
func (ftc *fileTypeChecker) MakeDir() error {
return ftc.mounter.MakeDir(ftc.path)
return ftc.hu.MakeDir(ftc.path)
}
func (ftc *fileTypeChecker) IsBlock() bool {
blkDevType, err := ftc.mounter.GetFileType(ftc.path)
blkDevType, err := ftc.hu.GetFileType(ftc.path)
if err != nil {
return false
}
@ -397,7 +403,7 @@ func (ftc *fileTypeChecker) IsBlock() bool {
}
func (ftc *fileTypeChecker) IsChar() bool {
charDevType, err := ftc.mounter.GetFileType(ftc.path)
charDevType, err := ftc.hu.GetFileType(ftc.path)
if err != nil {
return false
}
@ -405,7 +411,7 @@ func (ftc *fileTypeChecker) IsChar() bool {
}
func (ftc *fileTypeChecker) IsSocket() bool {
socketType, err := ftc.mounter.GetFileType(ftc.path)
socketType, err := ftc.hu.GetFileType(ftc.path)
if err != nil {
return false
}
@ -416,13 +422,13 @@ func (ftc *fileTypeChecker) GetPath() string {
return ftc.path
}
func newFileTypeChecker(path string, mounter mount.Interface) hostPathTypeChecker {
return &fileTypeChecker{path: path, mounter: mounter}
func newFileTypeChecker(path string, hu mount.HostUtils) hostPathTypeChecker {
return &fileTypeChecker{path: path, hu: hu}
}
// checkType checks whether the given path is the exact pathType
func checkType(path string, pathType *v1.HostPathType, mounter mount.Interface) error {
return checkTypeInternal(newFileTypeChecker(path, mounter), pathType)
func checkType(path string, pathType *v1.HostPathType, hu mount.HostUtils) error {
return checkTypeInternal(newFileTypeChecker(path, hu), pathType)
}
func checkTypeInternal(ftc hostPathTypeChecker, pathType *v1.HostPathType) error {

View File

@ -386,7 +386,7 @@ func TestOSFileTypeChecker(t *testing.T) {
}
for i, tc := range testCases {
fakeFTC := &utilmount.FakeMounter{
fakeFTC := &utilmount.FakeHostUtil{
Filesystem: map[string]utilmount.FileType{
tc.path: utilmount.FileType(tc.desiredType),
},

View File

@ -124,12 +124,18 @@ func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ vo
return nil, err
}
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
return &localVolumeMounter{
localVolume: &localVolume{
pod: pod,
podUID: pod.UID,
volName: spec.Name(),
mounter: plugin.host.GetMounter(plugin.GetPluginName()),
hostUtil: kvh.GetHostUtil(),
plugin: plugin,
globalPath: globalLocalPath,
MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(pod.UID, utilstrings.EscapeQualifiedName(localVolumePluginName), spec.Name())),
@ -230,7 +236,12 @@ func (plugin *localVolumePlugin) getGlobalLocalPath(spec *volume.Spec) (string,
return "", fmt.Errorf("local volume source is nil or local path is not set")
}
fileType, err := plugin.host.GetMounter(plugin.GetPluginName()).GetFileType(spec.PersistentVolume.Spec.Local.Path)
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return "", fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
fileType, err := kvh.GetHostUtil().GetFileType(spec.PersistentVolume.Spec.Local.Path)
if err != nil {
return "", err
}
@ -247,8 +258,9 @@ func (plugin *localVolumePlugin) getGlobalLocalPath(spec *volume.Spec) (string,
var _ volume.DeviceMountableVolumePlugin = &localVolumePlugin{}
type deviceMounter struct {
plugin *localVolumePlugin
mounter *mount.SafeFormatAndMount
plugin *localVolumePlugin
mounter *mount.SafeFormatAndMount
hostUtil mount.HostUtils
}
var _ volume.DeviceMounter = &deviceMounter{}
@ -258,9 +270,14 @@ func (plugin *localVolumePlugin) CanDeviceMount(spec *volume.Spec) (bool, error)
}
func (plugin *localVolumePlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
return &deviceMounter{
plugin: plugin,
mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
plugin: plugin,
mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
hostUtil: kvh.GetHostUtil(),
}, nil
}
@ -307,7 +324,7 @@ func (dm *deviceMounter) MountDevice(spec *volume.Spec, devicePath string, devic
if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 {
return fmt.Errorf("local volume source is nil or local path is not set")
}
fileType, err := dm.mounter.GetFileType(spec.PersistentVolume.Spec.Local.Path)
fileType, err := dm.hostUtil.GetFileType(spec.PersistentVolume.Spec.Local.Path)
if err != nil {
return err
}
@ -390,8 +407,9 @@ type localVolume struct {
// Global path to the volume
globalPath string
// Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface
plugin *localVolumePlugin
mounter mount.Interface
hostUtil mount.HostUtils
plugin *localVolumePlugin
volume.MetricsProvider
}
@ -462,7 +480,7 @@ func (m *localVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs)
refs = m.filterPodMounts(refs)
if len(refs) > 0 {
fsGroupNew := int64(*mounterArgs.FsGroup)
fsGroupOld, err := m.mounter.GetFSGroup(m.globalPath)
fsGroupOld, err := m.hostUtil.GetFSGroup(m.globalPath)
if err != nil {
return fmt.Errorf("failed to check fsGroup for %s (%v)", m.globalPath, err)
}

View File

@ -334,6 +334,8 @@ type KubeletVolumeHost interface {
CSIDriversSynced() cache.InformerSynced
// WaitForCacheSync is a helper function that waits for cache sync for CSIDriverLister
WaitForCacheSync() error
// Returns HostUtils Interface
GetHostUtil() mount.HostUtils
}
// AttachDetachVolumeHost is a AttachDetach Controller specific interface that plugins can use

View File

@ -374,8 +374,13 @@ func (plugin *rbdPlugin) newUnmounterInternal(volName string, podUID types.UID,
func (plugin *rbdPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
pluginMntDir := volutil.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil {
return nil, err
}

View File

@ -201,7 +201,12 @@ func (v *sioVolume) TearDownAt(dir string) error {
klog.V(4).Info(log("dir %s unmounted successfully", dir))
// detach/unmap
deviceBusy, err := mounter.DeviceOpened(dev)
kvh, ok := v.plugin.host.(volume.KubeletVolumeHost)
if !ok {
return fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
deviceBusy, err := hu.DeviceOpened(dev)
if err != nil {
klog.Error(log("teardown unable to get status for device %s: %v", dev, err))
return err

View File

@ -25,8 +25,10 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/listers/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",

View File

@ -33,8 +33,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
storagelisters "k8s.io/client-go/listers/storage/v1beta1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
utiltesting "k8s.io/client-go/util/testing"
cloudprovider "k8s.io/cloud-provider"
@ -68,11 +70,13 @@ type fakeVolumeHost struct {
pluginMgr VolumePluginMgr
cloud cloudprovider.Interface
mounter mount.Interface
hostUtil mount.HostUtils
exec mount.Exec
nodeLabels map[string]string
nodeName string
subpather subpath.Interface
csiDriverLister storagelisters.CSIDriverLister
informerFactory informers.SharedInformerFactory
}
var _ VolumeHost = &fakeVolumeHost{}
@ -103,12 +107,14 @@ func NewFakeVolumeHostWithCSINodeName(rootDir string, kubeClient clientset.Inter
func newFakeVolumeHost(rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, cloud cloudprovider.Interface, pathToTypeMap map[string]mount.FileType) *fakeVolumeHost {
host := &fakeVolumeHost{rootDir: rootDir, kubeClient: kubeClient, cloud: cloud}
host.mounter = &mount.FakeMounter{
host.mounter = &mount.FakeMounter{}
host.hostUtil = &mount.FakeHostUtil{
Filesystem: pathToTypeMap,
}
host.exec = mount.NewFakeExec(nil)
host.pluginMgr.InitPlugins(plugins, nil /* prober */, host)
host.subpather = &subpath.FakeSubpath{}
host.informerFactory = informers.NewSharedInformerFactory(kubeClient, time.Minute)
return host
}
@ -153,6 +159,10 @@ func (f *fakeVolumeHost) GetMounter(pluginName string) mount.Interface {
return f.mounter
}
func (f *fakeVolumeHost) GetHostUtil() mount.HostUtils {
return f.hostUtil
}
func (f *fakeVolumeHost) GetSubpather() subpath.Interface {
return f.subpather
}
@ -1495,11 +1505,28 @@ func (f *fakeVolumeHost) CSIDriverLister() storagelisters.CSIDriverLister {
return f.csiDriverLister
}
func (f *fakeVolumeHost) CSIDriversSynced() cache.InformerSynced {
// not needed for testing
return nil
}
func (f *fakeVolumeHost) CSINodeLister() storagelisters.CSINodeLister {
// not needed for testing
return nil
}
func (f *fakeVolumeHost) GetInformerFactory() informers.SharedInformerFactory {
return f.informerFactory
}
func (f *fakeVolumeHost) IsAttachDetachController() bool {
return true
}
func (f *fakeVolumeHost) SetKubeletError(err error) {
return
}
func (f *fakeVolumeHost) WaitForCacheSync() error {
return nil
}

View File

@ -20,7 +20,6 @@ package exec
import (
"fmt"
"os"
"k8s.io/klog"
@ -92,70 +91,15 @@ func (m *execMounter) List() ([]mount.MountPoint, error) {
return m.wrappedMounter.List()
}
func (m *execMounter) IsMountPointMatch(mp mount.MountPoint, dir string) bool {
return m.wrappedMounter.IsMountPointMatch(mp, dir)
}
// IsLikelyNotMountPoint determines whether a path is a mountpoint.
func (m *execMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return m.wrappedMounter.IsLikelyNotMountPoint(file)
}
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device.
func (m *execMounter) DeviceOpened(pathname string) (bool, error) {
return m.wrappedMounter.DeviceOpened(pathname)
}
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (m *execMounter) PathIsDevice(pathname string) (bool, error) {
return m.wrappedMounter.PathIsDevice(pathname)
}
//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (m *execMounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return m.wrappedMounter.GetDeviceNameFromMount(mountPath, pluginMountDir)
}
func (m *execMounter) IsMountPointMatch(mp mount.MountPoint, dir string) bool {
return m.wrappedMounter.IsMountPointMatch(mp, dir)
}
func (m *execMounter) MakeRShared(path string) error {
return m.wrappedMounter.MakeRShared(path)
}
func (m *execMounter) GetFileType(pathname string) (mount.FileType, error) {
return m.wrappedMounter.GetFileType(pathname)
}
func (m *execMounter) MakeFile(pathname string) error {
return m.wrappedMounter.MakeFile(pathname)
}
func (m *execMounter) MakeDir(pathname string) error {
return m.wrappedMounter.MakeDir(pathname)
}
func (m *execMounter) ExistsPath(pathname string) (bool, error) {
return m.wrappedMounter.ExistsPath(pathname)
}
func (m *execMounter) EvalHostSymlinks(pathname string) (string, error) {
return m.wrappedMounter.EvalHostSymlinks(pathname)
}
func (m *execMounter) GetMountRefs(pathname string) ([]string, error) {
return m.wrappedMounter.GetMountRefs(pathname)
}
func (m *execMounter) GetFSGroup(pathname string) (int64, error) {
return m.wrappedMounter.GetFSGroup(pathname)
}
func (m *execMounter) GetSELinuxSupport(pathname string) (bool, error) {
return m.wrappedMounter.GetSELinuxSupport(pathname)
}
func (m *execMounter) GetMode(pathname string) (os.FileMode, error) {
return m.wrappedMounter.GetMode(pathname)
}

View File

@ -20,13 +20,14 @@ package exec
import (
"errors"
"os"
"k8s.io/kubernetes/pkg/util/mount"
)
type execMounter struct{}
var _ = mount.Interface(&execMounter{})
// NewExecMounter returns a mounter that uses provided Exec interface to mount and
// unmount a filesystem. For all other calls it uses a wrapped mounter.
func NewExecMounter(exec mount.Exec, wrapped mount.Interface) mount.Interface {
@ -53,54 +54,6 @@ func (mounter *execMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil
}
func (mounter *execMounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return "", nil
}
func (mounter *execMounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
func (mounter *execMounter) PathIsDevice(pathname string) (bool, error) {
return true, nil
}
func (mounter *execMounter) MakeRShared(path string) error {
return nil
}
func (mounter *execMounter) GetFileType(pathname string) (mount.FileType, error) {
return mount.FileType("fake"), errors.New("not implemented")
}
func (mounter *execMounter) MakeDir(pathname string) error {
return nil
}
func (mounter *execMounter) MakeFile(pathname string) error {
return nil
}
func (mounter *execMounter) ExistsPath(pathname string) (bool, error) {
return true, errors.New("not implemented")
}
func (mounter *execMounter) EvalHostSymlinks(pathname string) (string, error) {
return "", errors.New("not implemented")
}
func (mounter *execMounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errors.New("not implemented")
}
func (mounter *execMounter) GetFSGroup(pathname string) (int64, error) {
return -1, errors.New("not implemented")
}
func (mounter *execMounter) GetSELinuxSupport(pathname string) (bool, error) {
return false, errors.New("not implemented")
}
func (mounter *execMounter) GetMode(pathname string) (os.FileMode, error) {
return 0, errors.New("not implemented")
}

View File

@ -65,6 +65,7 @@ go_test(
embed = [":go_default_library"],
deps = select({
"@io_bazel_rules_go//go/platform:linux": [
"//pkg/util/mount:go_default_library",
"//vendor/k8s.io/utils/nsenter:go_default_library",
],
"//conditions:default": [],

View File

@ -165,7 +165,8 @@ func (n *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
}
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts
resolvedFile, err := n.EvalHostSymlinks(file)
hu := NewHostUtil(n.ne, n.rootDir)
resolvedFile, err := hu.EvalHostSymlinks(file)
if err != nil {
return true, err
}
@ -211,36 +212,67 @@ func parseFindMnt(out string) (string, error) {
return out[:i], nil
}
// GetMountRefs finds all mount references to the path, returns a
// list of paths. Path could be a mountpoint path, device or a normal
// directory (for bind mount).
func (n *Mounter) GetMountRefs(pathname string) ([]string, error) {
pathExists, pathErr := mount.PathExists(pathname)
if !pathExists || mount.IsCorruptedMnt(pathErr) {
return []string{}, nil
} else if pathErr != nil {
return nil, fmt.Errorf("Error checking path %s: %v", pathname, pathErr)
}
hostpath, err := n.ne.EvalSymlinks(pathname, true /* mustExist */)
if err != nil {
return nil, err
}
return mount.SearchMountPoints(hostpath, hostProcMountinfoPath)
}
type hostUtil struct {
ne *nsenter.Nsenter
rootDir string
}
// hostUtil implements mount.HostUtils
var _ = mount.HostUtils(&hostUtil{})
// NewHostUtil returns a new mount.HostUtils implementation that works
// for kubelet running in a container
func NewHostUtil(ne *nsenter.Nsenter, rootDir string) mount.HostUtils {
return &hostUtil{ne: ne, rootDir: rootDir}
}
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device.
func (n *Mounter) DeviceOpened(pathname string) (bool, error) {
func (hu *hostUtil) DeviceOpened(pathname string) (bool, error) {
return mount.ExclusiveOpenFailsOnDevice(pathname)
}
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *Mounter) PathIsDevice(pathname string) (bool, error) {
pathType, err := n.GetFileType(pathname)
func (hu *hostUtil) PathIsDevice(pathname string) (bool, error) {
pathType, err := hu.GetFileType(pathname)
isDevice := pathType == mount.FileTypeCharDev || pathType == mount.FileTypeBlockDev
return isDevice, err
}
//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (n *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return mount.GetDeviceNameFromMountLinux(n, mountPath, pluginMountDir)
func (hu *hostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
return mount.GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
}
// MakeRShared checks if path is shared and bind-mounts it as rshared if needed.
func (n *Mounter) MakeRShared(path string) error {
func (hu *hostUtil) MakeRShared(path string) error {
return mount.DoMakeRShared(path, hostProcMountinfoPath)
}
// GetFileType checks for file/directory/socket/block/character devices.
func (n *Mounter) GetFileType(pathname string) (mount.FileType, error) {
func (hu *hostUtil) GetFileType(pathname string) (mount.FileType, error) {
var pathType mount.FileType
outputBytes, err := n.ne.Exec("stat", []string{"-L", "--printf=%F", pathname}).CombinedOutput()
outputBytes, err := hu.ne.Exec("stat", []string{"-L", "--printf=%F", pathname}).CombinedOutput()
if err != nil {
if strings.Contains(string(outputBytes), "No such file") {
err = fmt.Errorf("%s does not exist", pathname)
@ -267,18 +299,18 @@ func (n *Mounter) GetFileType(pathname string) (mount.FileType, error) {
}
// MakeDir creates a new directory.
func (n *Mounter) MakeDir(pathname string) error {
func (hu *hostUtil) MakeDir(pathname string) error {
args := []string{"-p", pathname}
if _, err := n.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
if _, err := hu.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
return err
}
return nil
}
// MakeFile creates an empty file.
func (n *Mounter) MakeFile(pathname string) error {
func (hu *hostUtil) MakeFile(pathname string) error {
args := []string{pathname}
if _, err := n.ne.Exec("touch", args).CombinedOutput(); err != nil {
if _, err := hu.ne.Exec("touch", args).CombinedOutput(); err != nil {
return err
}
return nil
@ -286,60 +318,43 @@ func (n *Mounter) MakeFile(pathname string) error {
// ExistsPath checks if pathname exists.
// Error is returned on any other error than "file not found".
func (n *Mounter) ExistsPath(pathname string) (bool, error) {
func (hu *hostUtil) ExistsPath(pathname string) (bool, error) {
// Resolve the symlinks but allow the target not to exist. EvalSymlinks
// would return an generic error when the target does not exist.
hostPath, err := n.ne.EvalSymlinks(pathname, false /* mustExist */)
hostPath, err := hu.ne.EvalSymlinks(pathname, false /* mustExist */)
if err != nil {
return false, err
}
kubeletpath := n.ne.KubeletPath(hostPath)
kubeletpath := hu.ne.KubeletPath(hostPath)
return utilpath.Exists(utilpath.CheckFollowSymlink, kubeletpath)
}
// EvalHostSymlinks returns the path name after evaluating symlinks.
func (n *Mounter) EvalHostSymlinks(pathname string) (string, error) {
return n.ne.EvalSymlinks(pathname, true)
}
// GetMountRefs finds all mount references to the path, returns a
// list of paths. Path could be a mountpoint path, device or a normal
// directory (for bind mount).
func (n *Mounter) GetMountRefs(pathname string) ([]string, error) {
pathExists, pathErr := mount.PathExists(pathname)
if !pathExists || mount.IsCorruptedMnt(pathErr) {
return []string{}, nil
} else if pathErr != nil {
return nil, fmt.Errorf("Error checking path %s: %v", pathname, pathErr)
}
hostpath, err := n.ne.EvalSymlinks(pathname, true /* mustExist */)
if err != nil {
return nil, err
}
return mount.SearchMountPoints(hostpath, hostProcMountinfoPath)
func (hu *hostUtil) EvalHostSymlinks(pathname string) (string, error) {
return hu.ne.EvalSymlinks(pathname, true)
}
// GetFSGroup returns FSGroup of pathname.
func (n *Mounter) GetFSGroup(pathname string) (int64, error) {
hostPath, err := n.ne.EvalSymlinks(pathname, true /* mustExist */)
func (hu *hostUtil) GetFSGroup(pathname string) (int64, error) {
hostPath, err := hu.ne.EvalSymlinks(pathname, true /* mustExist */)
if err != nil {
return -1, err
}
kubeletpath := n.ne.KubeletPath(hostPath)
kubeletpath := hu.ne.KubeletPath(hostPath)
return mount.GetFSGroupLinux(kubeletpath)
}
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux.
func (n *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
func (hu *hostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return mount.GetSELinux(pathname, hostProcMountsPath)
}
// GetMode returns permissions of pathname.
func (n *Mounter) GetMode(pathname string) (os.FileMode, error) {
hostPath, err := n.ne.EvalSymlinks(pathname, true /* mustExist */)
func (hu *hostUtil) GetMode(pathname string) (os.FileMode, error) {
hostPath, err := hu.ne.EvalSymlinks(pathname, true /* mustExist */)
if err != nil {
return 0, err
}
kubeletpath := n.ne.KubeletPath(hostPath)
kubeletpath := hu.ne.KubeletPath(hostPath)
return mount.GetModeLinux(kubeletpath)
}

View File

@ -25,6 +25,7 @@ import (
"path/filepath"
"testing"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/utils/nsenter"
)
@ -74,8 +75,9 @@ func TestParseFindMnt(t *testing.T) {
}
}
func newFakeNsenterMounter(tmpdir string, t *testing.T) (mounter *Mounter, rootfsPath string, varlibPath string, err error) {
rootfsPath = filepath.Join(tmpdir, "rootfs")
func newFakeNsenterHostUtil(tmpdir string, t *testing.T) (mount.HostUtils, string, string, error) {
rootfsPath := filepath.Join(tmpdir, "rootfs")
if err := os.Mkdir(rootfsPath, 0755); err != nil {
return nil, "", "", err
}
@ -84,12 +86,14 @@ func newFakeNsenterMounter(tmpdir string, t *testing.T) (mounter *Mounter, rootf
return nil, "", "", err
}
varlibPath = filepath.Join(tmpdir, "/var/lib/kubelet")
varlibPath := filepath.Join(tmpdir, "var/lib/kubelet")
if err := os.MkdirAll(varlibPath, 0755); err != nil {
return nil, "", "", err
}
return NewMounter(varlibPath, ne), rootfsPath, varlibPath, nil
hu := NewHostUtil(ne, varlibPath)
return hu, rootfsPath, varlibPath, nil
}
func TestNsenterExistsFile(t *testing.T) {
@ -262,7 +266,7 @@ func TestNsenterExistsFile(t *testing.T) {
continue
}
mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t)
hu, rootfs, _, err := newFakeNsenterHostUtil(tmpdir, t)
if err != nil {
t.Error(err)
continue
@ -274,7 +278,7 @@ func TestNsenterExistsFile(t *testing.T) {
continue
}
out, err := mounter.ExistsPath(path)
out, err := hu.ExistsPath(path)
if err != nil && !test.expectError {
t.Errorf("Test %q: unexpected error: %s", test.name, err)
}
@ -387,7 +391,7 @@ func TestNsenterGetMode(t *testing.T) {
continue
}
mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t)
hu, rootfs, _, err := newFakeNsenterHostUtil(tmpdir, t)
if err != nil {
t.Error(err)
continue
@ -399,7 +403,7 @@ func TestNsenterGetMode(t *testing.T) {
continue
}
mode, err := mounter.GetMode(path)
mode, err := hu.GetMode(path)
if err != nil && !test.expectError {
t.Errorf("Test %q: unexpected error: %s", test.name, err)
}

View File

@ -65,75 +65,87 @@ func (*Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil
}
// DeviceOpened checks if block device in use. I tis a noop for unsupported systems
func (*Mounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
// PathIsDevice checks if pathname refers to a device. It is a noop for unsupported
// systems
func (*Mounter) PathIsDevice(pathname string) (bool, error) {
return true, nil
}
// GetDeviceNameFromMount finds the device name from its global mount point using the
// given mountpath and plugin location. It is a noop of unsupported platforms
func (*Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) {
return "", nil
}
// MakeRShared checks if path is shared and bind-mounts it as rshared if needed.
// It is a noop on unsupported platforms
func (*Mounter) MakeRShared(path string) error {
return nil
}
// GetFileType checks for file/directory/socket/block/character devices.
// Always returns an error and "fake" filetype on unsupported platforms
func (*Mounter) GetFileType(_ string) (mount.FileType, error) {
return mount.FileType("fake"), errors.New("not implemented")
}
// MakeDir creates a new directory. Noop on unsupported platforms
func (*Mounter) MakeDir(pathname string) error {
return nil
}
// MakeFile creats an empty file. Noop on unsupported platforms
func (*Mounter) MakeFile(pathname string) error {
return nil
}
// ExistsPath checks if pathname exists. Always returns an error on unsupported
// platforms
func (*Mounter) ExistsPath(pathname string) (bool, error) {
return true, errors.New("not implemented")
}
// EvalHostSymlinks returns the path name after evaluating symlinks. Always
// returns an error on unsupported platforms
func (*Mounter) EvalHostSymlinks(pathname string) (string, error) {
return "", errors.New("not implemented")
}
// GetMountRefs finds all mount references to the path, returns a
// list of paths. Always returns an error on unsupported platforms
func (*Mounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errors.New("not implemented")
}
type hostUtil struct {
}
// hostUtil implements mount.HostUtils
var _ = mount.HostUtils(&hostUtil{})
// NewHostUtil returns a new implementation of mount.HostUtils for unsupported
// platforms
func NewHostUtil(ne *nsenter.Nsenter, rootDir string) mount.HostUtils {
return &hostUtil{}
}
// DeviceOpened checks if block device in use. I tis a noop for unsupported systems
func (*hostUtil) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
// PathIsDevice checks if pathname refers to a device. It is a noop for unsupported
// systems
func (*hostUtil) PathIsDevice(pathname string) (bool, error) {
return true, nil
}
// GetDeviceNameFromMount finds the device name from its global mount point using the
// given mountpath and plugin location. It is a noop of unsupported platforms
func (*hostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
return "", nil
}
// MakeRShared checks if path is shared and bind-mounts it as rshared if needed.
// It is a noop on unsupported platforms
func (*hostUtil) MakeRShared(path string) error {
return nil
}
// GetFileType checks for file/directory/socket/block/character devices.
// Always returns an error and "fake" filetype on unsupported platforms
func (*hostUtil) GetFileType(_ string) (mount.FileType, error) {
return mount.FileType("fake"), errors.New("not implemented")
}
// MakeDir creates a new directory. Noop on unsupported platforms
func (*hostUtil) MakeDir(pathname string) error {
return nil
}
// MakeFile creats an empty file. Noop on unsupported platforms
func (*hostUtil) MakeFile(pathname string) error {
return nil
}
// ExistsPath checks if pathname exists. Always returns an error on unsupported
// platforms
func (*hostUtil) ExistsPath(pathname string) (bool, error) {
return true, errors.New("not implemented")
}
// EvalHostSymlinks returns the path name after evaluating symlinks. Always
// returns an error on unsupported platforms
func (*hostUtil) EvalHostSymlinks(pathname string) (string, error) {
return "", errors.New("not implemented")
}
// GetFSGroup returns FSGroup of pathname. Always returns an error on unsupported platforms
func (*Mounter) GetFSGroup(pathname string) (int64, error) {
func (*hostUtil) GetFSGroup(pathname string) (int64, error) {
return -1, errors.New("not implemented")
}
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux.
// Always returns an error on unsupported platforms
func (*Mounter) GetSELinuxSupport(pathname string) (bool, error) {
func (*hostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return false, errors.New("not implemented")
}
// GetMode returns permissions of pathname. Always returns an error on unsupported platforms
func (*Mounter) GetMode(pathname string) (os.FileMode, error) {
func (*hostUtil) GetMode(pathname string) (os.FileMode, error) {
return 0, errors.New("not implemented")
}

View File

@ -64,7 +64,7 @@ func (f *fakeOGCounter) GenerateVolumesAreAttachedFunc(attachedVolumes []Attache
return f.recordFuncCall("GenerateVolumesAreAttachedFunc"), nil
}
func (f *fakeOGCounter) GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (volumetypes.GeneratedOperations, error) {
func (f *fakeOGCounter) GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, hu mount.HostUtils) (volumetypes.GeneratedOperations, error) {
return f.recordFuncCall("GenerateUnmountDeviceFunc"), nil
}
@ -80,7 +80,7 @@ func (f *fakeOGCounter) GenerateUnmapVolumeFunc(volumeToUnmount MountedVolume, a
return f.recordFuncCall("GenerateUnmapVolumeFunc"), nil
}
func (f *fakeOGCounter) GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (volumetypes.GeneratedOperations, error) {
func (f *fakeOGCounter) GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, hu mount.HostUtils) (volumetypes.GeneratedOperations, error) {
return f.recordFuncCall("GenerateUnmapDeviceFunc"), nil
}

View File

@ -123,7 +123,7 @@ type OperationExecutor interface {
// global map path. If number of reference is zero, remove global map path
// directory and free a volume for detach.
// It then updates the actual state of the world to reflect that.
UnmountDevice(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) error
UnmountDevice(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, hostutil mount.HostUtils) error
// VerifyControllerAttachedVolume checks if the specified volume is present
// in the specified nodes AttachedVolumes Status field. It uses kubeClient
@ -792,7 +792,7 @@ func (oe *operationExecutor) UnmountVolume(
func (oe *operationExecutor) UnmountDevice(
deviceToDetach AttachedVolume,
actualStateOfWorld ActualStateOfWorldMounterUpdater,
mounter mount.Interface) error {
hostutil mount.HostUtils) error {
fsVolume, err := util.CheckVolumeModeFilesystem(deviceToDetach.VolumeSpec)
if err != nil {
return err
@ -802,12 +802,12 @@ func (oe *operationExecutor) UnmountDevice(
// Filesystem volume case
// Unmount and detach a device if a volume isn't referenced
generatedOperations, err = oe.operationGenerator.GenerateUnmountDeviceFunc(
deviceToDetach, actualStateOfWorld, mounter)
deviceToDetach, actualStateOfWorld, hostutil)
} else {
// Block volume case
// Detach a device and remove loopback if a volume isn't referenced
generatedOperations, err = oe.operationGenerator.GenerateUnmapDeviceFunc(
deviceToDetach, actualStateOfWorld, mounter)
deviceToDetach, actualStateOfWorld, hostutil)
}
if err != nil {
return err

View File

@ -433,7 +433,7 @@ func (fopg *fakeOperationGenerator) GenerateVolumesAreAttachedFunc(attachedVolum
OperationFunc: opFunc,
}, nil
}
func (fopg *fakeOperationGenerator) GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (volumetypes.GeneratedOperations, error) {
func (fopg *fakeOperationGenerator) GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, hostutil mount.HostUtils) (volumetypes.GeneratedOperations, error) {
opFunc := func() (error, error) {
startOperationAndBlock(fopg.ch, fopg.quit)
return nil, nil
@ -506,7 +506,7 @@ func (fopg *fakeOperationGenerator) GenerateUnmapVolumeFunc(volumeToUnmount Moun
}, nil
}
func (fopg *fakeOperationGenerator) GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (volumetypes.GeneratedOperations, error) {
func (fopg *fakeOperationGenerator) GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, hostutil mount.HostUtils) (volumetypes.GeneratedOperations, error) {
opFunc := func() (error, error) {
startOperationAndBlock(fopg.ch, fopg.quit)
return nil, nil

View File

@ -106,7 +106,7 @@ type OperationGenerator interface {
GenerateVolumesAreAttachedFunc(attachedVolumes []AttachedVolume, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
// Generates the UnMountDevice function needed to perform the unmount of a device
GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (volumetypes.GeneratedOperations, error)
GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.HostUtils) (volumetypes.GeneratedOperations, error)
// Generates the function needed to check if the attach_detach controller has attached the volume plugin
GenerateVerifyControllerAttachedVolumeFunc(volumeToMount VolumeToMount, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
@ -118,7 +118,7 @@ type OperationGenerator interface {
GenerateUnmapVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error)
// Generates the UnmapDevice function needed to perform the unmap of a device
GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.Interface) (volumetypes.GeneratedOperations, error)
GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter mount.HostUtils) (volumetypes.GeneratedOperations, error)
// GetVolumePluginMgr returns volume plugin manager
GetVolumePluginMgr() *volume.VolumePluginMgr
@ -888,7 +888,7 @@ func (og *operationGenerator) GenerateUnmountVolumeFunc(
func (og *operationGenerator) GenerateUnmountDeviceFunc(
deviceToDetach AttachedVolume,
actualStateOfWorld ActualStateOfWorldMounterUpdater,
mounter mount.Interface) (volumetypes.GeneratedOperations, error) {
hostutil mount.HostUtils) (volumetypes.GeneratedOperations, error) {
var pluginName string
if useCSIPlugin(og.volumePluginMgr, deviceToDetach.VolumeSpec) {
@ -942,10 +942,10 @@ func (og *operationGenerator) GenerateUnmountDeviceFunc(
return deviceToDetach.GenerateError("UnmountDevice failed", unmountDeviceErr)
}
// Before logging that UnmountDevice succeeded and moving on,
// use mounter.PathIsDevice to check if the path is a device,
// if so use mounter.DeviceOpened to check if the device is in use anywhere
// use hostutil.PathIsDevice to check if the path is a device,
// if so use hostutil.DeviceOpened to check if the device is in use anywhere
// else on the system. Retry if it returns true.
deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, mounter)
deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, hostutil)
if deviceOpenedErr != nil {
return nil, deviceOpenedErr
}
@ -1080,8 +1080,12 @@ func (og *operationGenerator) GenerateMapVolumeFunc(
// kubelet, so evaluate it on the host and expect that it links to a device in /dev,
// which will be available to containerized kubelet. If still it does not exist,
// AttachFileDevice will fail. If kubelet is not containerized, eval it anyway.
mounter := og.GetVolumePluginMgr().Host.GetMounter(blockVolumePlugin.GetPluginName())
devicePath, err = mounter.EvalHostSymlinks(devicePath)
kvh, ok := og.GetVolumePluginMgr().Host.(volume.KubeletVolumeHost)
if !ok {
return volumeToMount.GenerateError("MapVolume type assertion error", fmt.Errorf("volume host does not implement KubeletVolumeHost interface"))
}
hu := kvh.GetHostUtil()
devicePath, err = hu.EvalHostSymlinks(devicePath)
if err != nil {
return volumeToMount.GenerateError("MapVolume.EvalHostSymlinks failed", err)
}
@ -1258,7 +1262,7 @@ func (og *operationGenerator) GenerateUnmapVolumeFunc(
func (og *operationGenerator) GenerateUnmapDeviceFunc(
deviceToDetach AttachedVolume,
actualStateOfWorld ActualStateOfWorldMounterUpdater,
mounter mount.Interface) (volumetypes.GeneratedOperations, error) {
hostutil mount.HostUtils) (volumetypes.GeneratedOperations, error) {
var blockVolumePlugin volume.BlockVolumePlugin
var err error
@ -1344,10 +1348,10 @@ func (og *operationGenerator) GenerateUnmapDeviceFunc(
}
// Before logging that UnmapDevice succeeded and moving on,
// use mounter.PathIsDevice to check if the path is a device,
// if so use mounter.DeviceOpened to check if the device is in use anywhere
// use hostutil.PathIsDevice to check if the path is a device,
// if so use hostutil.DeviceOpened to check if the device is in use anywhere
// else on the system. Retry if it returns true.
deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, mounter)
deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, hostutil)
if deviceOpenedErr != nil {
return nil, deviceOpenedErr
}
@ -1726,8 +1730,8 @@ func checkNodeAffinity(og *operationGenerator, volumeToMount VolumeToMount) erro
}
// isDeviceOpened checks the device status if the device is in use anywhere else on the system
func isDeviceOpened(deviceToDetach AttachedVolume, mounter mount.Interface) (bool, error) {
isDevicePath, devicePathErr := mounter.PathIsDevice(deviceToDetach.DevicePath)
func isDeviceOpened(deviceToDetach AttachedVolume, hostUtil mount.HostUtils) (bool, error) {
isDevicePath, devicePathErr := hostUtil.PathIsDevice(deviceToDetach.DevicePath)
var deviceOpened bool
var deviceOpenedErr error
if !isDevicePath && devicePathErr == nil ||
@ -1739,7 +1743,7 @@ func isDeviceOpened(deviceToDetach AttachedVolume, mounter mount.Interface) (boo
} else if devicePathErr != nil {
return false, deviceToDetach.GenerateErrorDetailed("PathIsDevice failed", devicePathErr)
} else {
deviceOpened, deviceOpenedErr = mounter.DeviceOpened(deviceToDetach.DevicePath)
deviceOpened, deviceOpenedErr = hostUtil.DeviceOpened(deviceToDetach.DevicePath)
if deviceOpenedErr != nil {
return false, deviceToDetach.GenerateErrorDetailed("DeviceOpened failed", deviceOpenedErr)
}

View File

@ -145,8 +145,13 @@ func (plugin *vsphereVolumePlugin) newUnmounterInternal(volName string, podUID t
func (plugin *vsphereVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
kvh, ok := plugin.host.(volume.KubeletVolumeHost)
if !ok {
return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
}
hu := kvh.GetHostUtil()
pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
volumePath, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
volumePath, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil {
return nil, err
}