local.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package local
  14. import (
  15. "fmt"
  16. "os"
  17. "path/filepath"
  18. "runtime"
  19. "strings"
  20. "k8s.io/klog"
  21. v1 "k8s.io/api/core/v1"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/types"
  24. "k8s.io/client-go/tools/record"
  25. "k8s.io/kubernetes/pkg/kubelet/events"
  26. "k8s.io/kubernetes/pkg/volume"
  27. "k8s.io/kubernetes/pkg/volume/util"
  28. "k8s.io/kubernetes/pkg/volume/util/hostutil"
  29. "k8s.io/kubernetes/pkg/volume/validation"
  30. "k8s.io/utils/keymutex"
  31. "k8s.io/utils/mount"
  32. utilstrings "k8s.io/utils/strings"
  33. )
  34. const (
  35. defaultFSType = "ext4"
  36. )
  37. // ProbeVolumePlugins is the primary entrypoint for volume plugins.
  38. func ProbeVolumePlugins() []volume.VolumePlugin {
  39. return []volume.VolumePlugin{&localVolumePlugin{}}
  40. }
  41. type localVolumePlugin struct {
  42. host volume.VolumeHost
  43. volumeLocks keymutex.KeyMutex
  44. recorder record.EventRecorder
  45. }
  46. var _ volume.VolumePlugin = &localVolumePlugin{}
  47. var _ volume.PersistentVolumePlugin = &localVolumePlugin{}
  48. var _ volume.BlockVolumePlugin = &localVolumePlugin{}
  49. const (
  50. localVolumePluginName = "kubernetes.io/local-volume"
  51. )
  52. func (plugin *localVolumePlugin) Init(host volume.VolumeHost) error {
  53. plugin.host = host
  54. plugin.volumeLocks = keymutex.NewHashed(0)
  55. plugin.recorder = host.GetEventRecorder()
  56. return nil
  57. }
  58. func (plugin *localVolumePlugin) GetPluginName() string {
  59. return localVolumePluginName
  60. }
  61. func (plugin *localVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  62. // This volume is only supported as a PersistentVolumeSource, so the PV name is unique
  63. return spec.Name(), nil
  64. }
  65. func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool {
  66. // This volume is only supported as a PersistentVolumeSource
  67. return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil)
  68. }
  69. func (plugin *localVolumePlugin) RequiresRemount() bool {
  70. return false
  71. }
  72. func (plugin *localVolumePlugin) SupportsMountOption() bool {
  73. return true
  74. }
  75. func (plugin *localVolumePlugin) SupportsBulkVolumeVerification() bool {
  76. return false
  77. }
  78. func (plugin *localVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  79. // The current meaning of AccessMode is how many nodes can attach to it, not how many pods can mount it
  80. return []v1.PersistentVolumeAccessMode{
  81. v1.ReadWriteOnce,
  82. }
  83. }
  84. func getVolumeSource(spec *volume.Spec) (*v1.LocalVolumeSource, bool, error) {
  85. if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil {
  86. return spec.PersistentVolume.Spec.Local, spec.ReadOnly, nil
  87. }
  88. return nil, false, fmt.Errorf("Spec does not reference a Local volume type")
  89. }
  90. func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  91. _, readOnly, err := getVolumeSource(spec)
  92. if err != nil {
  93. return nil, err
  94. }
  95. globalLocalPath, err := plugin.getGlobalLocalPath(spec)
  96. if err != nil {
  97. return nil, err
  98. }
  99. kvh, ok := plugin.host.(volume.KubeletVolumeHost)
  100. if !ok {
  101. return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
  102. }
  103. return &localVolumeMounter{
  104. localVolume: &localVolume{
  105. pod: pod,
  106. podUID: pod.UID,
  107. volName: spec.Name(),
  108. mounter: plugin.host.GetMounter(plugin.GetPluginName()),
  109. hostUtil: kvh.GetHostUtil(),
  110. plugin: plugin,
  111. globalPath: globalLocalPath,
  112. MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(pod.UID, utilstrings.EscapeQualifiedName(localVolumePluginName), spec.Name())),
  113. },
  114. mountOptions: util.MountOptionFromSpec(spec),
  115. readOnly: readOnly,
  116. }, nil
  117. }
  118. func (plugin *localVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  119. return &localVolumeUnmounter{
  120. localVolume: &localVolume{
  121. podUID: podUID,
  122. volName: volName,
  123. mounter: plugin.host.GetMounter(plugin.GetPluginName()),
  124. plugin: plugin,
  125. },
  126. }, nil
  127. }
  128. func (plugin *localVolumePlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod,
  129. _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) {
  130. volumeSource, readOnly, err := getVolumeSource(spec)
  131. if err != nil {
  132. return nil, err
  133. }
  134. return &localVolumeMapper{
  135. localVolume: &localVolume{
  136. podUID: pod.UID,
  137. volName: spec.Name(),
  138. globalPath: volumeSource.Path,
  139. plugin: plugin,
  140. },
  141. readOnly: readOnly,
  142. }, nil
  143. }
  144. func (plugin *localVolumePlugin) NewBlockVolumeUnmapper(volName string,
  145. podUID types.UID) (volume.BlockVolumeUnmapper, error) {
  146. return &localVolumeUnmapper{
  147. localVolume: &localVolume{
  148. podUID: podUID,
  149. volName: volName,
  150. plugin: plugin,
  151. },
  152. }, nil
  153. }
  154. // TODO: check if no path and no topology constraints are ok
  155. func (plugin *localVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  156. fs := v1.PersistentVolumeFilesystem
  157. // The main purpose of reconstructed volume is to clean unused mount points
  158. // and directories.
  159. // For filesystem volume with directory source, no global mount path is
  160. // needed to clean. Empty path is ok.
  161. // For filesystem volume with block source, we should resolve to its device
  162. // path if global mount path exists.
  163. var path string
  164. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  165. refs, err := mounter.GetMountRefs(mountPath)
  166. if err != nil {
  167. return nil, err
  168. }
  169. baseMountPath := plugin.generateBlockDeviceBaseGlobalPath()
  170. for _, ref := range refs {
  171. if mount.PathWithinBase(ref, baseMountPath) {
  172. // If the global mount for block device exists, the source is block
  173. // device.
  174. // The resolved device path may not be the exact same as path in
  175. // local PV object if symbolic link is used. However, it's the true
  176. // source and can be used in reconstructed volume.
  177. path, _, err = mount.GetDeviceNameFromMount(mounter, ref)
  178. if err != nil {
  179. return nil, err
  180. }
  181. klog.V(4).Infof("local: reconstructing volume %q (pod volume mount: %q) with device %q", volumeName, mountPath, path)
  182. break
  183. }
  184. }
  185. localVolume := &v1.PersistentVolume{
  186. ObjectMeta: metav1.ObjectMeta{
  187. Name: volumeName,
  188. },
  189. Spec: v1.PersistentVolumeSpec{
  190. PersistentVolumeSource: v1.PersistentVolumeSource{
  191. Local: &v1.LocalVolumeSource{
  192. Path: path,
  193. },
  194. },
  195. VolumeMode: &fs,
  196. },
  197. }
  198. return volume.NewSpecFromPersistentVolume(localVolume, false), nil
  199. }
  200. func (plugin *localVolumePlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName,
  201. mapPath string) (*volume.Spec, error) {
  202. block := v1.PersistentVolumeBlock
  203. localVolume := &v1.PersistentVolume{
  204. ObjectMeta: metav1.ObjectMeta{
  205. Name: volumeName,
  206. },
  207. Spec: v1.PersistentVolumeSpec{
  208. PersistentVolumeSource: v1.PersistentVolumeSource{
  209. Local: &v1.LocalVolumeSource{
  210. // Not needed because we don't need to detach local device from the host.
  211. Path: "",
  212. },
  213. },
  214. VolumeMode: &block,
  215. },
  216. }
  217. return volume.NewSpecFromPersistentVolume(localVolume, false), nil
  218. }
  219. func (plugin *localVolumePlugin) generateBlockDeviceBaseGlobalPath() string {
  220. return filepath.Join(plugin.host.GetPluginDir(localVolumePluginName), util.MountsInGlobalPDPath)
  221. }
  222. func (plugin *localVolumePlugin) getGlobalLocalPath(spec *volume.Spec) (string, error) {
  223. if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 {
  224. return "", fmt.Errorf("local volume source is nil or local path is not set")
  225. }
  226. kvh, ok := plugin.host.(volume.KubeletVolumeHost)
  227. if !ok {
  228. return "", fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
  229. }
  230. fileType, err := kvh.GetHostUtil().GetFileType(spec.PersistentVolume.Spec.Local.Path)
  231. if err != nil {
  232. return "", err
  233. }
  234. switch fileType {
  235. case hostutil.FileTypeDirectory:
  236. return spec.PersistentVolume.Spec.Local.Path, nil
  237. case hostutil.FileTypeBlockDev:
  238. return filepath.Join(plugin.generateBlockDeviceBaseGlobalPath(), spec.Name()), nil
  239. default:
  240. return "", fmt.Errorf("only directory and block device are supported")
  241. }
  242. }
  243. var _ volume.DeviceMountableVolumePlugin = &localVolumePlugin{}
  244. type deviceMounter struct {
  245. plugin *localVolumePlugin
  246. mounter *mount.SafeFormatAndMount
  247. hostUtil hostutil.HostUtils
  248. }
  249. var _ volume.DeviceMounter = &deviceMounter{}
  250. func (plugin *localVolumePlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
  251. return true, nil
  252. }
  253. func (plugin *localVolumePlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
  254. kvh, ok := plugin.host.(volume.KubeletVolumeHost)
  255. if !ok {
  256. return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
  257. }
  258. return &deviceMounter{
  259. plugin: plugin,
  260. mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
  261. hostUtil: kvh.GetHostUtil(),
  262. }, nil
  263. }
  264. func (dm *deviceMounter) mountLocalBlockDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
  265. klog.V(4).Infof("local: mounting device %s to %s", devicePath, deviceMountPath)
  266. notMnt, err := dm.mounter.IsLikelyNotMountPoint(deviceMountPath)
  267. if err != nil {
  268. if os.IsNotExist(err) {
  269. if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
  270. return err
  271. }
  272. notMnt = true
  273. } else {
  274. return err
  275. }
  276. }
  277. if !notMnt {
  278. return nil
  279. }
  280. fstype, err := getVolumeSourceFSType(spec)
  281. if err != nil {
  282. return err
  283. }
  284. ro, err := getVolumeSourceReadOnly(spec)
  285. if err != nil {
  286. return err
  287. }
  288. options := []string{}
  289. if ro {
  290. options = append(options, "ro")
  291. }
  292. mountOptions := util.MountOptionFromSpec(spec, options...)
  293. err = dm.mounter.FormatAndMount(devicePath, deviceMountPath, fstype, mountOptions)
  294. if err != nil {
  295. if rmErr := os.Remove(deviceMountPath); rmErr != nil {
  296. klog.Warningf("local: failed to remove %s: %v", deviceMountPath, rmErr)
  297. }
  298. return fmt.Errorf("local: failed to mount device %s at %s (fstype: %s), error %w", devicePath, deviceMountPath, fstype, err)
  299. }
  300. klog.V(3).Infof("local: successfully mount device %s at %s (fstype: %s)", devicePath, deviceMountPath, fstype)
  301. return nil
  302. }
  303. func (dm *deviceMounter) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
  304. if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 {
  305. return fmt.Errorf("local volume source is nil or local path is not set")
  306. }
  307. fileType, err := dm.hostUtil.GetFileType(spec.PersistentVolume.Spec.Local.Path)
  308. if err != nil {
  309. return err
  310. }
  311. switch fileType {
  312. case hostutil.FileTypeBlockDev:
  313. // local volume plugin does not implement AttachableVolumePlugin interface, so set devicePath to Path in PV spec directly
  314. devicePath = spec.PersistentVolume.Spec.Local.Path
  315. return dm.mountLocalBlockDevice(spec, devicePath, deviceMountPath)
  316. case hostutil.FileTypeDirectory:
  317. // if the given local volume path is of already filesystem directory, return directly
  318. return nil
  319. default:
  320. return fmt.Errorf("only directory and block device are supported")
  321. }
  322. }
  323. func getVolumeSourceFSType(spec *volume.Spec) (string, error) {
  324. if spec.PersistentVolume != nil &&
  325. spec.PersistentVolume.Spec.Local != nil {
  326. if spec.PersistentVolume.Spec.Local.FSType != nil {
  327. return *spec.PersistentVolume.Spec.Local.FSType, nil
  328. }
  329. // if the FSType is not set in local PV spec, setting it to default ("ext4")
  330. return defaultFSType, nil
  331. }
  332. return "", fmt.Errorf("spec does not reference a Local volume type")
  333. }
  334. func getVolumeSourceReadOnly(spec *volume.Spec) (bool, error) {
  335. if spec.PersistentVolume != nil &&
  336. spec.PersistentVolume.Spec.Local != nil {
  337. // local volumes used as a PersistentVolume gets the ReadOnly flag indirectly through
  338. // the persistent-claim volume used to mount the PV
  339. return spec.ReadOnly, nil
  340. }
  341. return false, fmt.Errorf("spec does not reference a Local volume type")
  342. }
  343. func (dm *deviceMounter) GetDeviceMountPath(spec *volume.Spec) (string, error) {
  344. return dm.plugin.getGlobalLocalPath(spec)
  345. }
  346. func (plugin *localVolumePlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
  347. return &deviceMounter{
  348. plugin: plugin,
  349. mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
  350. }, nil
  351. }
  352. func (plugin *localVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
  353. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  354. return mounter.GetMountRefs(deviceMountPath)
  355. }
  356. var _ volume.DeviceUnmounter = &deviceMounter{}
  357. func (dm *deviceMounter) UnmountDevice(deviceMountPath string) error {
  358. // If the local PV is a block device,
  359. // The deviceMountPath is generated to the format like :/var/lib/kubelet/plugins/kubernetes.io/local-volume/mounts/localpv.spec.Name;
  360. // If it is a filesystem directory, then the deviceMountPath is set directly to pvSpec.Local.Path
  361. // We only need to unmount block device here, so we need to check if the deviceMountPath passed here
  362. // has base mount path: /var/lib/kubelet/plugins/kubernetes.io/local-volume/mounts
  363. basemountPath := dm.plugin.generateBlockDeviceBaseGlobalPath()
  364. if mount.PathWithinBase(deviceMountPath, basemountPath) {
  365. return mount.CleanupMountPoint(deviceMountPath, dm.mounter, false)
  366. }
  367. return nil
  368. }
  369. // Local volumes represent a local directory on a node.
  370. // The directory at the globalPath will be bind-mounted to the pod's directory
  371. type localVolume struct {
  372. volName string
  373. pod *v1.Pod
  374. podUID types.UID
  375. // Global path to the volume
  376. globalPath string
  377. // Mounter interface that provides system calls to mount the global path to the pod local path.
  378. mounter mount.Interface
  379. hostUtil hostutil.HostUtils
  380. plugin *localVolumePlugin
  381. volume.MetricsProvider
  382. }
  383. func (l *localVolume) GetPath() string {
  384. return l.plugin.host.GetPodVolumeDir(l.podUID, utilstrings.EscapeQualifiedName(localVolumePluginName), l.volName)
  385. }
  386. type localVolumeMounter struct {
  387. *localVolume
  388. readOnly bool
  389. mountOptions []string
  390. }
  391. var _ volume.Mounter = &localVolumeMounter{}
  392. func (m *localVolumeMounter) GetAttributes() volume.Attributes {
  393. return volume.Attributes{
  394. ReadOnly: m.readOnly,
  395. Managed: !m.readOnly,
  396. SupportsSELinux: true,
  397. }
  398. }
  399. // CanMount checks prior to mount operations to verify that the required components (binaries, etc.)
  400. // to mount the volume are available on the underlying node.
  401. // If not, it returns an error
  402. func (m *localVolumeMounter) CanMount() error {
  403. return nil
  404. }
  405. // SetUp bind mounts the directory to the volume path
  406. func (m *localVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
  407. return m.SetUpAt(m.GetPath(), mounterArgs)
  408. }
  409. // SetUpAt bind mounts the directory to the volume path and sets up volume ownership
  410. func (m *localVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  411. m.plugin.volumeLocks.LockKey(m.globalPath)
  412. defer m.plugin.volumeLocks.UnlockKey(m.globalPath)
  413. if m.globalPath == "" {
  414. return fmt.Errorf("LocalVolume volume %q path is empty", m.volName)
  415. }
  416. err := validation.ValidatePathNoBacksteps(m.globalPath)
  417. if err != nil {
  418. return fmt.Errorf("invalid path: %s %v", m.globalPath, err)
  419. }
  420. notMnt, err := mount.IsNotMountPoint(m.mounter, dir)
  421. klog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly)
  422. if err != nil && !os.IsNotExist(err) {
  423. klog.Errorf("cannot validate mount point: %s %v", dir, err)
  424. return err
  425. }
  426. if !notMnt {
  427. return nil
  428. }
  429. refs, err := m.mounter.GetMountRefs(m.globalPath)
  430. if mounterArgs.FsGroup != nil {
  431. if err != nil {
  432. klog.Errorf("cannot collect mounting information: %s %v", m.globalPath, err)
  433. return err
  434. }
  435. // Only count mounts from other pods
  436. refs = m.filterPodMounts(refs)
  437. if len(refs) > 0 {
  438. fsGroupNew := int64(*mounterArgs.FsGroup)
  439. _, fsGroupOld, err := m.hostUtil.GetOwner(m.globalPath)
  440. if err != nil {
  441. return fmt.Errorf("failed to check fsGroup for %s (%v)", m.globalPath, err)
  442. }
  443. if fsGroupNew != fsGroupOld {
  444. m.plugin.recorder.Eventf(m.pod, v1.EventTypeWarning, events.WarnAlreadyMountedVolume, "The requested fsGroup is %d, but the volume %s has GID %d. The volume may not be shareable.", fsGroupNew, m.volName, fsGroupOld)
  445. }
  446. }
  447. }
  448. if runtime.GOOS != "windows" {
  449. // skip below MkdirAll for windows since the "bind mount" logic is implemented differently in mount_wiondows.go
  450. if err := os.MkdirAll(dir, 0750); err != nil {
  451. klog.Errorf("mkdir failed on disk %s (%v)", dir, err)
  452. return err
  453. }
  454. }
  455. // Perform a bind mount to the full path to allow duplicate mounts of the same volume.
  456. options := []string{"bind"}
  457. if m.readOnly {
  458. options = append(options, "ro")
  459. }
  460. mountOptions := util.JoinMountOptions(options, m.mountOptions)
  461. klog.V(4).Infof("attempting to mount %s", dir)
  462. globalPath := util.MakeAbsolutePath(runtime.GOOS, m.globalPath)
  463. err = m.mounter.Mount(globalPath, dir, "", mountOptions)
  464. if err != nil {
  465. klog.Errorf("Mount of volume %s failed: %v", dir, err)
  466. notMnt, mntErr := mount.IsNotMountPoint(m.mounter, dir)
  467. if mntErr != nil {
  468. klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
  469. return err
  470. }
  471. if !notMnt {
  472. if mntErr = m.mounter.Unmount(dir); mntErr != nil {
  473. klog.Errorf("Failed to unmount: %v", mntErr)
  474. return err
  475. }
  476. notMnt, mntErr = mount.IsNotMountPoint(m.mounter, dir)
  477. if mntErr != nil {
  478. klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
  479. return err
  480. }
  481. if !notMnt {
  482. // This is very odd, we don't expect it. We'll try again next sync loop.
  483. klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
  484. return err
  485. }
  486. }
  487. if rmErr := os.Remove(dir); rmErr != nil {
  488. klog.Warningf("failed to remove %s: %v", dir, rmErr)
  489. }
  490. return err
  491. }
  492. if !m.readOnly {
  493. // Volume owner will be written only once on the first volume mount
  494. if len(refs) == 0 {
  495. return volume.SetVolumeOwnership(m, mounterArgs.FsGroup)
  496. }
  497. }
  498. return nil
  499. }
  500. // filterPodMounts only returns mount paths inside the kubelet pod directory
  501. func (m *localVolumeMounter) filterPodMounts(refs []string) []string {
  502. filtered := []string{}
  503. for _, r := range refs {
  504. if strings.HasPrefix(r, m.plugin.host.GetPodsDir()+string(os.PathSeparator)) {
  505. filtered = append(filtered, r)
  506. }
  507. }
  508. return filtered
  509. }
  510. type localVolumeUnmounter struct {
  511. *localVolume
  512. }
  513. var _ volume.Unmounter = &localVolumeUnmounter{}
  514. // TearDown unmounts the bind mount
  515. func (u *localVolumeUnmounter) TearDown() error {
  516. return u.TearDownAt(u.GetPath())
  517. }
  518. // TearDownAt unmounts the bind mount
  519. func (u *localVolumeUnmounter) TearDownAt(dir string) error {
  520. klog.V(4).Infof("Unmounting volume %q at path %q\n", u.volName, dir)
  521. return mount.CleanupMountPoint(dir, u.mounter, true) /* extensiveMountPointCheck = true */
  522. }
  523. // localVolumeMapper implements the BlockVolumeMapper interface for local volumes.
  524. type localVolumeMapper struct {
  525. *localVolume
  526. readOnly bool
  527. }
  528. var _ volume.BlockVolumeMapper = &localVolumeMapper{}
  529. var _ volume.CustomBlockVolumeMapper = &localVolumeMapper{}
  530. // SetUpDevice prepares the volume to the node by the plugin specific way.
  531. func (m *localVolumeMapper) SetUpDevice() error {
  532. return nil
  533. }
  534. // MapPodDevice provides physical device path for the local PV.
  535. func (m *localVolumeMapper) MapPodDevice() (string, error) {
  536. globalPath := util.MakeAbsolutePath(runtime.GOOS, m.globalPath)
  537. klog.V(4).Infof("MapPodDevice returning path %s", globalPath)
  538. return globalPath, nil
  539. }
  540. // localVolumeUnmapper implements the BlockVolumeUnmapper interface for local volumes.
  541. type localVolumeUnmapper struct {
  542. *localVolume
  543. }
  544. var _ volume.BlockVolumeUnmapper = &localVolumeUnmapper{}
  545. // GetGlobalMapPath returns global map path and error.
  546. // path: plugins/kubernetes.io/kubernetes.io/local-volume/volumeDevices/{volumeName}
  547. func (l *localVolume) GetGlobalMapPath(spec *volume.Spec) (string, error) {
  548. return filepath.Join(l.plugin.host.GetVolumeDevicePluginDir(utilstrings.EscapeQualifiedName(localVolumePluginName)),
  549. l.volName), nil
  550. }
  551. // GetPodDeviceMapPath returns pod device map path and volume name.
  552. // path: pods/{podUid}/volumeDevices/kubernetes.io~local-volume
  553. // volName: local-pv-ff0d6d4
  554. func (l *localVolume) GetPodDeviceMapPath() (string, string) {
  555. return l.plugin.host.GetPodVolumeDeviceDir(l.podUID,
  556. utilstrings.EscapeQualifiedName(localVolumePluginName)), l.volName
  557. }