fake_mounter.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. Copyright 2015 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 mount
  14. import (
  15. "os"
  16. "path/filepath"
  17. "sync"
  18. "k8s.io/klog"
  19. )
  20. // FakeMounter implements mount.Interface for tests.
  21. type FakeMounter struct {
  22. MountPoints []MountPoint
  23. log []FakeAction
  24. // Error to return for a path when calling IsLikelyNotMountPoint
  25. MountCheckErrors map[string]error
  26. // Some tests run things in parallel, make sure the mounter does not produce
  27. // any golang's DATA RACE warnings.
  28. mutex sync.Mutex
  29. UnmountFunc UnmountFunc
  30. }
  31. // UnmountFunc is a function callback to be executed during the Unmount() call.
  32. type UnmountFunc func(path string) error
  33. var _ Interface = &FakeMounter{}
  34. const (
  35. // FakeActionMount is the string for specifying mount as FakeAction.Action
  36. FakeActionMount = "mount"
  37. // FakeActionUnmount is the string for specifying unmount as FakeAction.Action
  38. FakeActionUnmount = "unmount"
  39. )
  40. // FakeAction objects are logged every time a fake mount or unmount is called.
  41. type FakeAction struct {
  42. Action string // "mount" or "unmount"
  43. Target string // applies to both mount and unmount actions
  44. Source string // applies only to "mount" actions
  45. FSType string // applies only to "mount" actions
  46. }
  47. // NewFakeMounter returns a FakeMounter struct that implements Interface and is
  48. // suitable for testing purposes.
  49. func NewFakeMounter(mps []MountPoint) *FakeMounter {
  50. return &FakeMounter{
  51. MountPoints: mps,
  52. }
  53. }
  54. // ResetLog clears all the log entries in FakeMounter
  55. func (f *FakeMounter) ResetLog() {
  56. f.mutex.Lock()
  57. defer f.mutex.Unlock()
  58. f.log = []FakeAction{}
  59. }
  60. // GetLog returns the slice of FakeActions taken by the mounter
  61. func (f *FakeMounter) GetLog() []FakeAction {
  62. f.mutex.Lock()
  63. defer f.mutex.Unlock()
  64. return f.log
  65. }
  66. // Mount records the mount event and updates the in-memory mount points for FakeMounter
  67. func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
  68. f.mutex.Lock()
  69. defer f.mutex.Unlock()
  70. opts := []string{}
  71. for _, option := range options {
  72. // find 'bind' option
  73. if option == "bind" {
  74. // This is a bind-mount. In order to mimic linux behaviour, we must
  75. // use the original device of the bind-mount as the real source.
  76. // E.g. when mounted /dev/sda like this:
  77. // $ mount /dev/sda /mnt/test
  78. // $ mount -o bind /mnt/test /mnt/bound
  79. // then /proc/mount contains:
  80. // /dev/sda /mnt/test
  81. // /dev/sda /mnt/bound
  82. // (and not /mnt/test /mnt/bound)
  83. // I.e. we must use /dev/sda as source instead of /mnt/test in the
  84. // bind mount.
  85. for _, mnt := range f.MountPoints {
  86. if source == mnt.Path {
  87. source = mnt.Device
  88. break
  89. }
  90. }
  91. }
  92. // reuse MountPoint.Opts field to mark mount as readonly
  93. opts = append(opts, option)
  94. }
  95. // If target is a symlink, get its absolute path
  96. absTarget, err := filepath.EvalSymlinks(target)
  97. if err != nil {
  98. absTarget = target
  99. }
  100. f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: opts})
  101. klog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
  102. f.log = append(f.log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
  103. return nil
  104. }
  105. // Unmount records the unmount event and updates the in-memory mount points for FakeMounter
  106. func (f *FakeMounter) Unmount(target string) error {
  107. f.mutex.Lock()
  108. defer f.mutex.Unlock()
  109. // If target is a symlink, get its absolute path
  110. absTarget, err := filepath.EvalSymlinks(target)
  111. if err != nil {
  112. absTarget = target
  113. }
  114. newMountpoints := []MountPoint{}
  115. for _, mp := range f.MountPoints {
  116. if mp.Path == absTarget {
  117. if f.UnmountFunc != nil {
  118. err := f.UnmountFunc(absTarget)
  119. if err != nil {
  120. return err
  121. }
  122. }
  123. klog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, absTarget)
  124. // Don't copy it to newMountpoints
  125. continue
  126. }
  127. newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
  128. }
  129. f.MountPoints = newMountpoints
  130. f.log = append(f.log, FakeAction{Action: FakeActionUnmount, Target: absTarget})
  131. delete(f.MountCheckErrors, target)
  132. return nil
  133. }
  134. // List returns all the in-memory mountpoints for FakeMounter
  135. func (f *FakeMounter) List() ([]MountPoint, error) {
  136. f.mutex.Lock()
  137. defer f.mutex.Unlock()
  138. return f.MountPoints, nil
  139. }
  140. // IsLikelyNotMountPoint determines whether a path is a mountpoint by checking
  141. // if the absolute path to file is in the in-memory mountpoints
  142. func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
  143. f.mutex.Lock()
  144. defer f.mutex.Unlock()
  145. err := f.MountCheckErrors[file]
  146. if err != nil {
  147. return false, err
  148. }
  149. _, err = os.Stat(file)
  150. if err != nil {
  151. return true, err
  152. }
  153. // If file is a symlink, get its absolute path
  154. absFile, err := filepath.EvalSymlinks(file)
  155. if err != nil {
  156. absFile = file
  157. }
  158. for _, mp := range f.MountPoints {
  159. if mp.Path == absFile {
  160. klog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
  161. return false, nil
  162. }
  163. }
  164. klog.V(5).Infof("isLikelyNotMountPoint for %s: true", file)
  165. return true, nil
  166. }
  167. // GetMountRefs finds all mount references to the path, returns a
  168. // list of paths.
  169. func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
  170. realpath, err := filepath.EvalSymlinks(pathname)
  171. if err != nil {
  172. // Ignore error in FakeMounter, because we actually didn't create files.
  173. realpath = pathname
  174. }
  175. return getMountRefsByDev(f, realpath)
  176. }