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

View File

@ -250,6 +250,7 @@ type Dependencies struct {
OnHeartbeatFailure func() OnHeartbeatFailure func()
KubeClient clientset.Interface KubeClient clientset.Interface
Mounter mount.Interface Mounter mount.Interface
HostUtil mount.HostUtils
OOMAdjuster *oom.OOMAdjuster OOMAdjuster *oom.OOMAdjuster
OSInterface kubecontainer.OSInterface OSInterface kubecontainer.OSInterface
PodConfig *config.PodConfig PodConfig *config.PodConfig
@ -518,6 +519,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
cgroupsPerQOS: kubeCfg.CgroupsPerQOS, cgroupsPerQOS: kubeCfg.CgroupsPerQOS,
cgroupRoot: kubeCfg.CgroupRoot, cgroupRoot: kubeCfg.CgroupRoot,
mounter: kubeDeps.Mounter, mounter: kubeDeps.Mounter,
hostutil: kubeDeps.HostUtil,
subpather: kubeDeps.Subpather, subpather: kubeDeps.Subpather,
maxPods: int(kubeCfg.MaxPods), maxPods: int(kubeCfg.MaxPods),
podsPerCore: int(kubeCfg.PodsPerCore), podsPerCore: int(kubeCfg.PodsPerCore),
@ -812,6 +814,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
klet.volumePluginMgr, klet.volumePluginMgr,
klet.containerRuntime, klet.containerRuntime,
kubeDeps.Mounter, kubeDeps.Mounter,
kubeDeps.HostUtil,
klet.getPodsDir(), klet.getPodsDir(),
kubeDeps.Recorder, kubeDeps.Recorder,
experimentalCheckNodeCapabilitiesBeforeMount, experimentalCheckNodeCapabilitiesBeforeMount,
@ -1096,6 +1099,9 @@ type Kubelet struct {
// Mounter to use for volumes. // Mounter to use for volumes.
mounter mount.Interface mounter mount.Interface
// hostutil to interact with filesystems
hostutil mount.HostUtils
// subpather to execute subpath actions // subpather to execute subpath actions
subpather subpath.Interface subpather subpath.Interface
@ -1229,7 +1235,7 @@ func (kl *Kubelet) setupDataDirs() error {
if err := os.MkdirAll(kl.getRootDir(), 0750); err != nil { if err := os.MkdirAll(kl.getRootDir(), 0750); err != nil {
return fmt.Errorf("error creating root directory: %v", err) 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) return fmt.Errorf("error configuring root directory: %v", err)
} }
if err := os.MkdirAll(kl.getPodsDir(), 0750); err != nil { 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. // 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: // Kubernetes only mounts on /etc/hosts if:
// - container is not an infrastructure (pause) container // - container is not an infrastructure (pause) container
// - container is not already mounting on /etc/hosts // - 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 volumePath := hostPath
hostPath = filepath.Join(volumePath, subPath) 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) klog.Errorf("Could not determine if subPath %s exists; will not attempt to change its permissions", hostPath)
} else if !subPathExists { } else if !subPathExists {
// Create the sub path now because if it's auto-created later when referenced, it may have an // 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 // 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 // later auto-create it with the incorrect mode 0750
// Make extra care not to escape the volume! // Make extra care not to escape the volume!
perm, err := mounter.GetMode(volumePath) perm, err := hu.GetMode(volumePath)
if err != nil { if err != nil {
return nil, cleanupAction, err return nil, cleanupAction, err
} }
@ -465,7 +465,7 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *v1.Pod, container *v1.Contai
} }
opts.Envs = append(opts.Envs, envs...) 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 { if err != nil {
return nil, cleanupAction, err return nil, cleanupAction, err
} }

View File

@ -241,7 +241,7 @@ func TestMakeMounts(t *testing.T) {
for name, tc := range testCases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
fm := &mount.FakeMounter{} fhu := &mount.FakeHostUtil{}
fsp := &subpath.FakeSubpath{} fsp := &subpath.FakeSubpath{}
pod := v1.Pod{ pod := v1.Pod{
Spec: v1.PodSpec{ 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 // validate only the error if we expect an error
if tc.expectErr { if tc.expectErr {

View File

@ -53,7 +53,7 @@ import (
) )
func TestDisabledSubpath(t *testing.T) { func TestDisabledSubpath(t *testing.T) {
fm := &mount.FakeMounter{} fhu := &mount.FakeHostUtil{}
fsp := &subpath.FakeSubpath{} fsp := &subpath.FakeSubpath{}
pod := v1.Pod{ pod := v1.Pod{
Spec: v1.PodSpec{ Spec: v1.PodSpec{
@ -97,7 +97,7 @@ func TestDisabledSubpath(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, false)()
for name, test := range cases { 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 { if err != nil && !test.expectError {
t.Errorf("test %v failed: %v", name, err) 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{} 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{ expectedMounts := []kubecontainer.Mount{
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,6 @@ import (
type FakeMounter struct { type FakeMounter struct {
MountPoints []MountPoint MountPoints []MountPoint
Log []FakeAction Log []FakeAction
Filesystem map[string]FileType
// Error to return for a path when calling IsLikelyNotMountPoint // Error to return for a path when calling IsLikelyNotMountPoint
MountCheckErrors map[string]error MountCheckErrors map[string]error
// Some tests run things in parallel, make sure the mounter does not produce // 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 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) { func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
realpath, err := filepath.EvalSymlinks(pathname) realpath, err := filepath.EvalSymlinks(pathname)
if err != nil { if err != nil {
@ -225,14 +174,73 @@ func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
return getMountRefsByDev(f, realpath) 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") 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") 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") return 0, errors.New("not implemented")
} }

View File

@ -55,6 +55,13 @@ type Interface interface {
// IsLikelyNotMountPoint does NOT properly detect all mountpoint types // IsLikelyNotMountPoint does NOT properly detect all mountpoint types
// most notably linux bind mounts and symbolic link. // most notably linux bind mounts and symbolic link.
IsLikelyNotMountPoint(file string) (bool, error) 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 // DeviceOpened determines if the device is in use elsewhere
// on the system, i.e. still mounted. // on the system, i.e. still mounted.
DeviceOpened(pathname string) (bool, error) DeviceOpened(pathname string) (bool, error)
@ -62,7 +69,7 @@ type Interface interface {
PathIsDevice(pathname string) (bool, error) PathIsDevice(pathname string) (bool, error)
// GetDeviceNameFromMount finds the device name by checking the mount path // GetDeviceNameFromMount finds the device name by checking the mount path
// to get the global mount path within its plugin directory // 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 // MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. If not, it bind-mounts the path as rshared. // propagation. If not, it bind-mounts the path as rshared.
MakeRShared(path string) error MakeRShared(path string) error
@ -81,10 +88,6 @@ type Interface interface {
// EvalHostSymlinks returns the path name after evaluating symlinks. // EvalHostSymlinks returns the path name after evaluating symlinks.
// Will operate in the host mount namespace if kubelet is running in a container. // Will operate in the host mount namespace if kubelet is running in a container.
EvalHostSymlinks(pathname string) (string, error) 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 returns FSGroup of the path.
GetFSGroup(pathname string) (int64, error) GetFSGroup(pathname string) (int64, error)
// GetSELinuxSupport returns true if given path is on a mount that supports // GetSELinuxSupport returns true if given path is on a mount that supports
@ -123,6 +126,10 @@ type Exec interface {
// the mount interface // the mount interface
var _ Interface = &Mounter{} 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. // This represents a single line in /proc/mounts or /etc/fstab.
type MountPoint struct { type MountPoint struct {
Device string 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 // 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 { if err != nil {
return true, err return true, err
} }

View File

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

View File

@ -249,179 +249,21 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil return true, nil
} }
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag. func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
// If pathname is not a device, log and return false with nil error. pathExists, pathErr := PathExists(pathname)
// If open returns errno EBUSY, return true with nil error. if !pathExists {
// If open returns nil, return false with nil error. return []string{}, nil
// Otherwise, return false with error } else if IsCorruptedMnt(pathErr) {
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) { klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
return ExclusiveOpenFailsOnDevice(pathname) return []string{}, nil
} } else if pathErr != nil {
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
// 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
} }
// err in call to os.Stat realpath, err := filepath.EvalSymlinks(pathname)
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)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return parseProcMounts(content) return SearchMountPoints(realpath, procMountInfoPath)
}
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)
} }
// formatAndMount uses unix utils to format and mount the given disk // 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 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 // isShared returns true, if given path is on a mount point that has shared
// mount propagation. // mount propagation.
func isShared(mount string, mountInfoPath string) (bool, error) { func isShared(mount string, mountInfoPath string) (bool, error) {
@ -726,28 +750,11 @@ func GetSELinux(path string, mountInfoFilename string) (bool, error) {
return false, nil return false, nil
} }
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { func (hu *hostUtil) GetSELinuxSupport(pathname string) (bool, 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) {
return GetSELinux(pathname, procMountInfoPath) 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) realpath, err := filepath.EvalSymlinks(pathname)
if err != nil { if err != nil {
return 0, err return 0, err
@ -755,7 +762,7 @@ func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
return GetFSGroupLinux(realpath) return GetFSGroupLinux(realpath)
} }
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) { func (hu *hostUtil) GetMode(pathname string) (os.FileMode, error) {
return GetModeLinux(pathname) return GetModeLinux(pathname)
} }

View File

@ -661,7 +661,7 @@ func createSocketFile(socketDir string) (string, error) {
} }
func TestGetFileType(t *testing.T) { func TestGetFileType(t *testing.T) {
mounter := Mounter{"fake/path", false} hu := NewHostUtil()
testCase := []struct { testCase := []struct {
name string name string
@ -750,7 +750,7 @@ func TestGetFileType(t *testing.T) {
defer os.RemoveAll(cleanUpPath) defer os.RemoveAll(cleanUpPath)
} }
fileType, err := mounter.GetFileType(path) fileType, err := hu.GetFileType(path)
if err != nil { if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) 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 return true, unsupportedErr
} }
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) { func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
return "", unsupportedErr return nil, errors.New("not implemented")
}
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 *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { 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 return true, unsupportedErr
} }
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) { func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, 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) {
return "", unsupportedErr return "", unsupportedErr
} }
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { type hostUtil struct{}
return nil, errors.New("not implemented")
func NewHostUtil() HostUtils {
return &hostUtil{}
} }
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { // DeviceOpened determines if the device is in use elsewhere
return -1, errors.New("not implemented") func (hu *hostUtil) DeviceOpened(pathname string) (bool, error) {
return false, unsupportedErr
} }
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { // PathIsDevice determines if a path is a device.
return false, errors.New("not implemented") func (hu *hostUtil) PathIsDevice(pathname string) (bool, error) {
return true, unsupportedErr
} }
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) { // GetDeviceNameFromMount finds the device name by checking the mount path
return 0, errors.New("not implemented") // 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 { if err != nil {
return true, fmt.Errorf("readlink error: %v", err) return true, fmt.Errorf("readlink error: %v", err)
} }
exists, err := mounter.ExistsPath(target) hu := NewHostUtil()
exists, err := hu.ExistsPath(target)
if err != nil { if err != nil {
return true, err return true, err
} }
@ -195,90 +196,19 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil return true, nil
} }
// GetDeviceNameFromMount given a mnt point, find the device // GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) { func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir) windowsPath := normalizeWindowsPath(pathname)
} pathExists, pathErr := PathExists(windowsPath)
if !pathExists {
// getDeviceNameFromMount find the device(drive) name in which return []string{}, nil
// the mount path reference should match the given plugin mount directory. In case no mount path reference } else if IsCorruptedMnt(pathErr) {
// matches, returns the volume name taken from its given mountPath klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
func getDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) { return []string{}, nil
refs, err := mounter.GetMountRefs(mountPath) } else if pathErr != nil {
if err != nil { return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
} }
if len(refs) == 0 { return []string{pathname}, nil
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)
} }
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { 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 return links, nil
} }
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows type hostUtil struct{}
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
windowsPath := normalizeWindowsPath(pathname) func NewHostUtil() HostUtils {
pathExists, pathErr := PathExists(windowsPath) return &hostUtil{}
if !pathExists { }
return []string{}, nil
} else if IsCorruptedMnt(pathErr) { // GetDeviceNameFromMount given a mnt point, find the device
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath) func (hu *hostUtil) GetDeviceNameFromMount(mounter Interface, mountPath, pluginMountDir string) (string, error) {
return []string{}, nil return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
} else if pathErr != nil { }
return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
// 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 // Note that on windows, it always returns 0. We actually don't set FSGroup on
// windows platform, see SetVolumeOwnership implementation. // windows platform, see SetVolumeOwnership implementation.
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { func (hu *hostUtil) GetFSGroup(pathname string) (int64, error) {
return 0, nil return 0, nil
} }
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { func (hu *hostUtil) GetSELinuxSupport(pathname string) (bool, error) {
// Windows does not support SELinux. // Windows does not support SELinux.
return false, nil 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) info, err := os.Stat(pathname)
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -175,7 +175,7 @@ func TestPathWithinBase(t *testing.T) {
} }
func TestGetFileType(t *testing.T) { func TestGetFileType(t *testing.T) {
mounter := New("fake/path") hu := NewHostUtil()
testCase := []struct { testCase := []struct {
name string name string
@ -213,7 +213,7 @@ func TestGetFileType(t *testing.T) {
defer os.RemoveAll(cleanUpPath) defer os.RemoveAll(cleanUpPath)
} }
fileType, err := mounter.GetFileType(path) fileType, err := hu.GetFileType(path)
if err != nil { if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err) 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) { func (plugin *awsElasticBlockStorePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName()) 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()) pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
volumeID, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) volumeID, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -309,8 +309,13 @@ var _ volume.NodeExpandableVolumePlugin = &azureDataDiskPlugin{}
func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) { func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName()) 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()) pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil { if err != nil {
return nil, err 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) { func (plugin *cinderPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName()) 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()) pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

@ -391,6 +391,12 @@ func (p *csiPlugin) NewMounter(
return nil, errors.New("failed to get a Kubernetes client") 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{ mounter := &csiMountMgr{
plugin: p, plugin: p,
k8s: k8s, k8s: k8s,
@ -402,6 +408,7 @@ func (p *csiPlugin) NewMounter(
volumeID: volumeHandle, volumeID: volumeHandle,
specVolumeID: spec.Name(), specVolumeID: spec.Name(),
readOnly: readOnly, readOnly: readOnly,
kubeVolHost: kvh,
} }
mounter.csiClientGetter.driverName = csiDriverName(driverName) 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) { 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)) 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{ unmounter := &csiMountMgr{
plugin: p, plugin: p,
podUID: podUID, podUID: podUID,
specVolumeID: specName, specVolumeID: specName,
kubeVolHost: kvh,
} }
// load volume info from file // 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) { func (plugin *gcePersistentDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName()) 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()) pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

@ -386,7 +386,7 @@ func TestOSFileTypeChecker(t *testing.T) {
} }
for i, tc := range testCases { for i, tc := range testCases {
fakeFTC := &utilmount.FakeMounter{ fakeFTC := &utilmount.FakeHostUtil{
Filesystem: map[string]utilmount.FileType{ Filesystem: map[string]utilmount.FileType{
tc.path: utilmount.FileType(tc.desiredType), 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 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{ return &localVolumeMounter{
localVolume: &localVolume{ localVolume: &localVolume{
pod: pod, pod: pod,
podUID: pod.UID, podUID: pod.UID,
volName: spec.Name(), volName: spec.Name(),
mounter: plugin.host.GetMounter(plugin.GetPluginName()), mounter: plugin.host.GetMounter(plugin.GetPluginName()),
hostUtil: kvh.GetHostUtil(),
plugin: plugin, plugin: plugin,
globalPath: globalLocalPath, globalPath: globalLocalPath,
MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(pod.UID, utilstrings.EscapeQualifiedName(localVolumePluginName), spec.Name())), 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") 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 { if err != nil {
return "", err return "", err
} }
@ -247,8 +258,9 @@ func (plugin *localVolumePlugin) getGlobalLocalPath(spec *volume.Spec) (string,
var _ volume.DeviceMountableVolumePlugin = &localVolumePlugin{} var _ volume.DeviceMountableVolumePlugin = &localVolumePlugin{}
type deviceMounter struct { type deviceMounter struct {
plugin *localVolumePlugin plugin *localVolumePlugin
mounter *mount.SafeFormatAndMount mounter *mount.SafeFormatAndMount
hostUtil mount.HostUtils
} }
var _ volume.DeviceMounter = &deviceMounter{} var _ volume.DeviceMounter = &deviceMounter{}
@ -258,9 +270,14 @@ func (plugin *localVolumePlugin) CanDeviceMount(spec *volume.Spec) (bool, error)
} }
func (plugin *localVolumePlugin) NewDeviceMounter() (volume.DeviceMounter, 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{ return &deviceMounter{
plugin: plugin, plugin: plugin,
mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
hostUtil: kvh.GetHostUtil(),
}, nil }, 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 { 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") 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 { if err != nil {
return err return err
} }
@ -390,8 +407,9 @@ type localVolume struct {
// Global path to the volume // Global path to the volume
globalPath string globalPath string
// Mounter interface that provides system calls to mount the global path to the pod local path. // Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface mounter mount.Interface
plugin *localVolumePlugin hostUtil mount.HostUtils
plugin *localVolumePlugin
volume.MetricsProvider volume.MetricsProvider
} }
@ -462,7 +480,7 @@ func (m *localVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs)
refs = m.filterPodMounts(refs) refs = m.filterPodMounts(refs)
if len(refs) > 0 { if len(refs) > 0 {
fsGroupNew := int64(*mounterArgs.FsGroup) fsGroupNew := int64(*mounterArgs.FsGroup)
fsGroupOld, err := m.mounter.GetFSGroup(m.globalPath) fsGroupOld, err := m.hostUtil.GetFSGroup(m.globalPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to check fsGroup for %s (%v)", m.globalPath, err) 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 CSIDriversSynced() cache.InformerSynced
// WaitForCacheSync is a helper function that waits for cache sync for CSIDriverLister // WaitForCacheSync is a helper function that waits for cache sync for CSIDriverLister
WaitForCacheSync() error WaitForCacheSync() error
// Returns HostUtils Interface
GetHostUtil() mount.HostUtils
} }
// AttachDetachVolumeHost is a AttachDetach Controller specific interface that plugins can use // 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) { func (plugin *rbdPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName()) 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()) pluginMntDir := volutil.GetPluginMountDir(plugin.host, plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil { if err != nil {
return nil, err 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)) klog.V(4).Info(log("dir %s unmounted successfully", dir))
// detach/unmap // 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 { if err != nil {
klog.Error(log("teardown unable to get status for device %s: %v", dev, err)) klog.Error(log("teardown unable to get status for device %s: %v", dev, err))
return 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/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types: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/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/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/listers/storage/v1beta1: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/tools/record:go_default_library",
"//staging/src/k8s.io/client-go/util/testing:go_default_library", "//staging/src/k8s.io/client-go/util/testing:go_default_library",
"//staging/src/k8s.io/cloud-provider: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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
storagelisters "k8s.io/client-go/listers/storage/v1beta1" storagelisters "k8s.io/client-go/listers/storage/v1beta1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
utiltesting "k8s.io/client-go/util/testing" utiltesting "k8s.io/client-go/util/testing"
cloudprovider "k8s.io/cloud-provider" cloudprovider "k8s.io/cloud-provider"
@ -68,11 +70,13 @@ type fakeVolumeHost struct {
pluginMgr VolumePluginMgr pluginMgr VolumePluginMgr
cloud cloudprovider.Interface cloud cloudprovider.Interface
mounter mount.Interface mounter mount.Interface
hostUtil mount.HostUtils
exec mount.Exec exec mount.Exec
nodeLabels map[string]string nodeLabels map[string]string
nodeName string nodeName string
subpather subpath.Interface subpather subpath.Interface
csiDriverLister storagelisters.CSIDriverLister csiDriverLister storagelisters.CSIDriverLister
informerFactory informers.SharedInformerFactory
} }
var _ VolumeHost = &fakeVolumeHost{} 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 { 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 := &fakeVolumeHost{rootDir: rootDir, kubeClient: kubeClient, cloud: cloud}
host.mounter = &mount.FakeMounter{ host.mounter = &mount.FakeMounter{}
host.hostUtil = &mount.FakeHostUtil{
Filesystem: pathToTypeMap, Filesystem: pathToTypeMap,
} }
host.exec = mount.NewFakeExec(nil) host.exec = mount.NewFakeExec(nil)
host.pluginMgr.InitPlugins(plugins, nil /* prober */, host) host.pluginMgr.InitPlugins(plugins, nil /* prober */, host)
host.subpather = &subpath.FakeSubpath{} host.subpather = &subpath.FakeSubpath{}
host.informerFactory = informers.NewSharedInformerFactory(kubeClient, time.Minute)
return host return host
} }
@ -153,6 +159,10 @@ func (f *fakeVolumeHost) GetMounter(pluginName string) mount.Interface {
return f.mounter return f.mounter
} }
func (f *fakeVolumeHost) GetHostUtil() mount.HostUtils {
return f.hostUtil
}
func (f *fakeVolumeHost) GetSubpather() subpath.Interface { func (f *fakeVolumeHost) GetSubpather() subpath.Interface {
return f.subpather return f.subpather
} }
@ -1495,11 +1505,28 @@ func (f *fakeVolumeHost) CSIDriverLister() storagelisters.CSIDriverLister {
return f.csiDriverLister return f.csiDriverLister
} }
func (f *fakeVolumeHost) CSIDriversSynced() cache.InformerSynced {
// not needed for testing
return nil
}
func (f *fakeVolumeHost) CSINodeLister() storagelisters.CSINodeLister { func (f *fakeVolumeHost) CSINodeLister() storagelisters.CSINodeLister {
// not needed for testing // not needed for testing
return nil return nil
} }
func (f *fakeVolumeHost) GetInformerFactory() informers.SharedInformerFactory {
return f.informerFactory
}
func (f *fakeVolumeHost) IsAttachDetachController() bool { func (f *fakeVolumeHost) IsAttachDetachController() bool {
return true 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 ( import (
"fmt" "fmt"
"os"
"k8s.io/klog" "k8s.io/klog"
@ -92,70 +91,15 @@ func (m *execMounter) List() ([]mount.MountPoint, error) {
return m.wrappedMounter.List() 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. // IsLikelyNotMountPoint determines whether a path is a mountpoint.
func (m *execMounter) IsLikelyNotMountPoint(file string) (bool, error) { func (m *execMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return m.wrappedMounter.IsLikelyNotMountPoint(file) 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) { func (m *execMounter) GetMountRefs(pathname string) ([]string, error) {
return m.wrappedMounter.GetMountRefs(pathname) 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 ( import (
"errors" "errors"
"os"
"k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/mount"
) )
type execMounter struct{} type execMounter struct{}
var _ = mount.Interface(&execMounter{})
// NewExecMounter returns a mounter that uses provided Exec interface to mount and // NewExecMounter returns a mounter that uses provided Exec interface to mount and
// unmount a filesystem. For all other calls it uses a wrapped mounter. // unmount a filesystem. For all other calls it uses a wrapped mounter.
func NewExecMounter(exec mount.Exec, wrapped mount.Interface) mount.Interface { 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 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) { func (mounter *execMounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errors.New("not implemented") 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"], embed = [":go_default_library"],
deps = select({ deps = select({
"@io_bazel_rules_go//go/platform:linux": [ "@io_bazel_rules_go//go/platform:linux": [
"//pkg/util/mount:go_default_library",
"//vendor/k8s.io/utils/nsenter:go_default_library", "//vendor/k8s.io/utils/nsenter:go_default_library",
], ],
"//conditions:default": [], "//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 // 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 { if err != nil {
return true, err return true, err
} }
@ -211,36 +212,67 @@ func parseFindMnt(out string) (string, error) {
return out[:i], nil 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. // 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 true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY. // Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device. // 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) return mount.ExclusiveOpenFailsOnDevice(pathname)
} }
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers // PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device. // to a device.
func (n *Mounter) PathIsDevice(pathname string) (bool, error) { func (hu *hostUtil) PathIsDevice(pathname string) (bool, error) {
pathType, err := n.GetFileType(pathname) pathType, err := hu.GetFileType(pathname)
isDevice := pathType == mount.FileTypeCharDev || pathType == mount.FileTypeBlockDev isDevice := pathType == mount.FileTypeCharDev || pathType == mount.FileTypeBlockDev
return isDevice, err return isDevice, err
} }
//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts //GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (n *Mounter) GetDeviceNameFromMount(mountPath, pluginMountDir string) (string, error) { func (hu *hostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
return mount.GetDeviceNameFromMountLinux(n, mountPath, pluginMountDir) return mount.GetDeviceNameFromMountLinux(mounter, mountPath, pluginMountDir)
} }
// MakeRShared checks if path is shared and bind-mounts it as rshared if needed. // 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) return mount.DoMakeRShared(path, hostProcMountinfoPath)
} }
// GetFileType checks for file/directory/socket/block/character devices. // 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 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 err != nil {
if strings.Contains(string(outputBytes), "No such file") { if strings.Contains(string(outputBytes), "No such file") {
err = fmt.Errorf("%s does not exist", pathname) 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. // MakeDir creates a new directory.
func (n *Mounter) MakeDir(pathname string) error { func (hu *hostUtil) MakeDir(pathname string) error {
args := []string{"-p", pathname} 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 err
} }
return nil return nil
} }
// MakeFile creates an empty file. // MakeFile creates an empty file.
func (n *Mounter) MakeFile(pathname string) error { func (hu *hostUtil) MakeFile(pathname string) error {
args := []string{pathname} 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 err
} }
return nil return nil
@ -286,60 +318,43 @@ func (n *Mounter) MakeFile(pathname string) error {
// ExistsPath checks if pathname exists. // ExistsPath checks if pathname exists.
// Error is returned on any other error than "file not found". // 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 // Resolve the symlinks but allow the target not to exist. EvalSymlinks
// would return an generic error when the target does not exist. // 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 { if err != nil {
return false, err return false, err
} }
kubeletpath := n.ne.KubeletPath(hostPath) kubeletpath := hu.ne.KubeletPath(hostPath)
return utilpath.Exists(utilpath.CheckFollowSymlink, kubeletpath) return utilpath.Exists(utilpath.CheckFollowSymlink, kubeletpath)
} }
// EvalHostSymlinks returns the path name after evaluating symlinks. // EvalHostSymlinks returns the path name after evaluating symlinks.
func (n *Mounter) EvalHostSymlinks(pathname string) (string, error) { func (hu *hostUtil) EvalHostSymlinks(pathname string) (string, error) {
return n.ne.EvalSymlinks(pathname, true) return hu.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)
} }
// GetFSGroup returns FSGroup of pathname. // GetFSGroup returns FSGroup of pathname.
func (n *Mounter) GetFSGroup(pathname string) (int64, error) { func (hu *hostUtil) GetFSGroup(pathname string) (int64, error) {
hostPath, err := n.ne.EvalSymlinks(pathname, true /* mustExist */) hostPath, err := hu.ne.EvalSymlinks(pathname, true /* mustExist */)
if err != nil { if err != nil {
return -1, err return -1, err
} }
kubeletpath := n.ne.KubeletPath(hostPath) kubeletpath := hu.ne.KubeletPath(hostPath)
return mount.GetFSGroupLinux(kubeletpath) return mount.GetFSGroupLinux(kubeletpath)
} }
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux. // 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) return mount.GetSELinux(pathname, hostProcMountsPath)
} }
// GetMode returns permissions of pathname. // GetMode returns permissions of pathname.
func (n *Mounter) GetMode(pathname string) (os.FileMode, error) { func (hu *hostUtil) GetMode(pathname string) (os.FileMode, error) {
hostPath, err := n.ne.EvalSymlinks(pathname, true /* mustExist */) hostPath, err := hu.ne.EvalSymlinks(pathname, true /* mustExist */)
if err != nil { if err != nil {
return 0, err return 0, err
} }
kubeletpath := n.ne.KubeletPath(hostPath) kubeletpath := hu.ne.KubeletPath(hostPath)
return mount.GetModeLinux(kubeletpath) return mount.GetModeLinux(kubeletpath)
} }

View File

@ -25,6 +25,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/utils/nsenter" "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) { func newFakeNsenterHostUtil(tmpdir string, t *testing.T) (mount.HostUtils, string, string, error) {
rootfsPath = filepath.Join(tmpdir, "rootfs") rootfsPath := filepath.Join(tmpdir, "rootfs")
if err := os.Mkdir(rootfsPath, 0755); err != nil { if err := os.Mkdir(rootfsPath, 0755); err != nil {
return nil, "", "", err return nil, "", "", err
} }
@ -84,12 +86,14 @@ func newFakeNsenterMounter(tmpdir string, t *testing.T) (mounter *Mounter, rootf
return nil, "", "", err 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 { if err := os.MkdirAll(varlibPath, 0755); err != nil {
return nil, "", "", err return nil, "", "", err
} }
return NewMounter(varlibPath, ne), rootfsPath, varlibPath, nil hu := NewHostUtil(ne, varlibPath)
return hu, rootfsPath, varlibPath, nil
} }
func TestNsenterExistsFile(t *testing.T) { func TestNsenterExistsFile(t *testing.T) {
@ -262,7 +266,7 @@ func TestNsenterExistsFile(t *testing.T) {
continue continue
} }
mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t) hu, rootfs, _, err := newFakeNsenterHostUtil(tmpdir, t)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
continue continue
@ -274,7 +278,7 @@ func TestNsenterExistsFile(t *testing.T) {
continue continue
} }
out, err := mounter.ExistsPath(path) out, err := hu.ExistsPath(path)
if err != nil && !test.expectError { if err != nil && !test.expectError {
t.Errorf("Test %q: unexpected error: %s", test.name, err) t.Errorf("Test %q: unexpected error: %s", test.name, err)
} }
@ -387,7 +391,7 @@ func TestNsenterGetMode(t *testing.T) {
continue continue
} }
mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t) hu, rootfs, _, err := newFakeNsenterHostUtil(tmpdir, t)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
continue continue
@ -399,7 +403,7 @@ func TestNsenterGetMode(t *testing.T) {
continue continue
} }
mode, err := mounter.GetMode(path) mode, err := hu.GetMode(path)
if err != nil && !test.expectError { if err != nil && !test.expectError {
t.Errorf("Test %q: unexpected error: %s", test.name, err) 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 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 // GetMountRefs finds all mount references to the path, returns a
// list of paths. Always returns an error on unsupported platforms // list of paths. Always returns an error on unsupported platforms
func (*Mounter) GetMountRefs(pathname string) ([]string, error) { func (*Mounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errors.New("not implemented") 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 // 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") return -1, errors.New("not implemented")
} }
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux. // GetSELinuxSupport tests if pathname is on a mount that supports SELinux.
// Always returns an error on unsupported platforms // 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") return false, errors.New("not implemented")
} }
// GetMode returns permissions of pathname. Always returns an error on unsupported platforms // 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") return 0, errors.New("not implemented")
} }

View File

@ -64,7 +64,7 @@ func (f *fakeOGCounter) GenerateVolumesAreAttachedFunc(attachedVolumes []Attache
return f.recordFuncCall("GenerateVolumesAreAttachedFunc"), nil 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 return f.recordFuncCall("GenerateUnmountDeviceFunc"), nil
} }
@ -80,7 +80,7 @@ func (f *fakeOGCounter) GenerateUnmapVolumeFunc(volumeToUnmount MountedVolume, a
return f.recordFuncCall("GenerateUnmapVolumeFunc"), nil 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 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 // global map path. If number of reference is zero, remove global map path
// directory and free a volume for detach. // directory and free a volume for detach.
// It then updates the actual state of the world to reflect that. // 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 // VerifyControllerAttachedVolume checks if the specified volume is present
// in the specified nodes AttachedVolumes Status field. It uses kubeClient // in the specified nodes AttachedVolumes Status field. It uses kubeClient
@ -792,7 +792,7 @@ func (oe *operationExecutor) UnmountVolume(
func (oe *operationExecutor) UnmountDevice( func (oe *operationExecutor) UnmountDevice(
deviceToDetach AttachedVolume, deviceToDetach AttachedVolume,
actualStateOfWorld ActualStateOfWorldMounterUpdater, actualStateOfWorld ActualStateOfWorldMounterUpdater,
mounter mount.Interface) error { hostutil mount.HostUtils) error {
fsVolume, err := util.CheckVolumeModeFilesystem(deviceToDetach.VolumeSpec) fsVolume, err := util.CheckVolumeModeFilesystem(deviceToDetach.VolumeSpec)
if err != nil { if err != nil {
return err return err
@ -802,12 +802,12 @@ func (oe *operationExecutor) UnmountDevice(
// Filesystem volume case // Filesystem volume case
// Unmount and detach a device if a volume isn't referenced // Unmount and detach a device if a volume isn't referenced
generatedOperations, err = oe.operationGenerator.GenerateUnmountDeviceFunc( generatedOperations, err = oe.operationGenerator.GenerateUnmountDeviceFunc(
deviceToDetach, actualStateOfWorld, mounter) deviceToDetach, actualStateOfWorld, hostutil)
} else { } else {
// Block volume case // Block volume case
// Detach a device and remove loopback if a volume isn't referenced // Detach a device and remove loopback if a volume isn't referenced
generatedOperations, err = oe.operationGenerator.GenerateUnmapDeviceFunc( generatedOperations, err = oe.operationGenerator.GenerateUnmapDeviceFunc(
deviceToDetach, actualStateOfWorld, mounter) deviceToDetach, actualStateOfWorld, hostutil)
} }
if err != nil { if err != nil {
return err return err

View File

@ -433,7 +433,7 @@ func (fopg *fakeOperationGenerator) GenerateVolumesAreAttachedFunc(attachedVolum
OperationFunc: opFunc, OperationFunc: opFunc,
}, nil }, 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) { opFunc := func() (error, error) {
startOperationAndBlock(fopg.ch, fopg.quit) startOperationAndBlock(fopg.ch, fopg.quit)
return nil, nil return nil, nil
@ -506,7 +506,7 @@ func (fopg *fakeOperationGenerator) GenerateUnmapVolumeFunc(volumeToUnmount Moun
}, nil }, 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) { opFunc := func() (error, error) {
startOperationAndBlock(fopg.ch, fopg.quit) startOperationAndBlock(fopg.ch, fopg.quit)
return nil, nil return nil, nil

View File

@ -106,7 +106,7 @@ type OperationGenerator interface {
GenerateVolumesAreAttachedFunc(attachedVolumes []AttachedVolume, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) GenerateVolumesAreAttachedFunc(attachedVolumes []AttachedVolume, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
// Generates the UnMountDevice function needed to perform the unmount of a device // 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 // 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) 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) GenerateUnmapVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error)
// Generates the UnmapDevice function needed to perform the unmap of a device // 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 returns volume plugin manager
GetVolumePluginMgr() *volume.VolumePluginMgr GetVolumePluginMgr() *volume.VolumePluginMgr
@ -888,7 +888,7 @@ func (og *operationGenerator) GenerateUnmountVolumeFunc(
func (og *operationGenerator) GenerateUnmountDeviceFunc( func (og *operationGenerator) GenerateUnmountDeviceFunc(
deviceToDetach AttachedVolume, deviceToDetach AttachedVolume,
actualStateOfWorld ActualStateOfWorldMounterUpdater, actualStateOfWorld ActualStateOfWorldMounterUpdater,
mounter mount.Interface) (volumetypes.GeneratedOperations, error) { hostutil mount.HostUtils) (volumetypes.GeneratedOperations, error) {
var pluginName string var pluginName string
if useCSIPlugin(og.volumePluginMgr, deviceToDetach.VolumeSpec) { if useCSIPlugin(og.volumePluginMgr, deviceToDetach.VolumeSpec) {
@ -942,10 +942,10 @@ func (og *operationGenerator) GenerateUnmountDeviceFunc(
return deviceToDetach.GenerateError("UnmountDevice failed", unmountDeviceErr) return deviceToDetach.GenerateError("UnmountDevice failed", unmountDeviceErr)
} }
// Before logging that UnmountDevice succeeded and moving on, // Before logging that UnmountDevice succeeded and moving on,
// use mounter.PathIsDevice to check if the path is a device, // use hostutil.PathIsDevice to check if the path is a device,
// if so use mounter.DeviceOpened to check if the device is in use anywhere // if so use hostutil.DeviceOpened to check if the device is in use anywhere
// else on the system. Retry if it returns true. // else on the system. Retry if it returns true.
deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, mounter) deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, hostutil)
if deviceOpenedErr != nil { if deviceOpenedErr != nil {
return nil, deviceOpenedErr 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, // 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, // which will be available to containerized kubelet. If still it does not exist,
// AttachFileDevice will fail. If kubelet is not containerized, eval it anyway. // AttachFileDevice will fail. If kubelet is not containerized, eval it anyway.
mounter := og.GetVolumePluginMgr().Host.GetMounter(blockVolumePlugin.GetPluginName()) kvh, ok := og.GetVolumePluginMgr().Host.(volume.KubeletVolumeHost)
devicePath, err = mounter.EvalHostSymlinks(devicePath) 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 { if err != nil {
return volumeToMount.GenerateError("MapVolume.EvalHostSymlinks failed", err) return volumeToMount.GenerateError("MapVolume.EvalHostSymlinks failed", err)
} }
@ -1258,7 +1262,7 @@ func (og *operationGenerator) GenerateUnmapVolumeFunc(
func (og *operationGenerator) GenerateUnmapDeviceFunc( func (og *operationGenerator) GenerateUnmapDeviceFunc(
deviceToDetach AttachedVolume, deviceToDetach AttachedVolume,
actualStateOfWorld ActualStateOfWorldMounterUpdater, actualStateOfWorld ActualStateOfWorldMounterUpdater,
mounter mount.Interface) (volumetypes.GeneratedOperations, error) { hostutil mount.HostUtils) (volumetypes.GeneratedOperations, error) {
var blockVolumePlugin volume.BlockVolumePlugin var blockVolumePlugin volume.BlockVolumePlugin
var err error var err error
@ -1344,10 +1348,10 @@ func (og *operationGenerator) GenerateUnmapDeviceFunc(
} }
// Before logging that UnmapDevice succeeded and moving on, // Before logging that UnmapDevice succeeded and moving on,
// use mounter.PathIsDevice to check if the path is a device, // use hostutil.PathIsDevice to check if the path is a device,
// if so use mounter.DeviceOpened to check if the device is in use anywhere // if so use hostutil.DeviceOpened to check if the device is in use anywhere
// else on the system. Retry if it returns true. // else on the system. Retry if it returns true.
deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, mounter) deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, hostutil)
if deviceOpenedErr != nil { if deviceOpenedErr != nil {
return nil, deviceOpenedErr 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 // 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) { func isDeviceOpened(deviceToDetach AttachedVolume, hostUtil mount.HostUtils) (bool, error) {
isDevicePath, devicePathErr := mounter.PathIsDevice(deviceToDetach.DevicePath) isDevicePath, devicePathErr := hostUtil.PathIsDevice(deviceToDetach.DevicePath)
var deviceOpened bool var deviceOpened bool
var deviceOpenedErr error var deviceOpenedErr error
if !isDevicePath && devicePathErr == nil || if !isDevicePath && devicePathErr == nil ||
@ -1739,7 +1743,7 @@ func isDeviceOpened(deviceToDetach AttachedVolume, mounter mount.Interface) (boo
} else if devicePathErr != nil { } else if devicePathErr != nil {
return false, deviceToDetach.GenerateErrorDetailed("PathIsDevice failed", devicePathErr) return false, deviceToDetach.GenerateErrorDetailed("PathIsDevice failed", devicePathErr)
} else { } else {
deviceOpened, deviceOpenedErr = mounter.DeviceOpened(deviceToDetach.DevicePath) deviceOpened, deviceOpenedErr = hostUtil.DeviceOpened(deviceToDetach.DevicePath)
if deviceOpenedErr != nil { if deviceOpenedErr != nil {
return false, deviceToDetach.GenerateErrorDetailed("DeviceOpened failed", deviceOpenedErr) 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) { func (plugin *vsphereVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName()) 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()) pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
volumePath, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) volumePath, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }