...

Source file src/k8s.io/utils/mount/fake_mounter.go

Documentation: k8s.io/utils/mount

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package mount
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  	"sync"
    23  
    24  	"k8s.io/klog/v2"
    25  )
    26  
    27  // FakeMounter implements mount.Interface for tests.
    28  type FakeMounter struct {
    29  	MountPoints []MountPoint
    30  	log         []FakeAction
    31  	// Error to return for a path when calling IsLikelyNotMountPoint
    32  	MountCheckErrors map[string]error
    33  	// Some tests run things in parallel, make sure the mounter does not produce
    34  	// any golang's DATA RACE warnings.
    35  	mutex       sync.Mutex
    36  	UnmountFunc UnmountFunc
    37  }
    38  
    39  // UnmountFunc is a function callback to be executed during the Unmount() call.
    40  type UnmountFunc func(path string) error
    41  
    42  var _ Interface = &FakeMounter{}
    43  
    44  const (
    45  	// FakeActionMount is the string for specifying mount as FakeAction.Action
    46  	FakeActionMount = "mount"
    47  	// FakeActionUnmount is the string for specifying unmount as FakeAction.Action
    48  	FakeActionUnmount = "unmount"
    49  )
    50  
    51  // FakeAction objects are logged every time a fake mount or unmount is called.
    52  type FakeAction struct {
    53  	Action string // "mount" or "unmount"
    54  	Target string // applies to both mount and unmount actions
    55  	Source string // applies only to "mount" actions
    56  	FSType string // applies only to "mount" actions
    57  }
    58  
    59  // NewFakeMounter returns a FakeMounter struct that implements Interface and is
    60  // suitable for testing purposes.
    61  func NewFakeMounter(mps []MountPoint) *FakeMounter {
    62  	return &FakeMounter{
    63  		MountPoints: mps,
    64  	}
    65  }
    66  
    67  // ResetLog clears all the log entries in FakeMounter
    68  func (f *FakeMounter) ResetLog() {
    69  	f.mutex.Lock()
    70  	defer f.mutex.Unlock()
    71  
    72  	f.log = []FakeAction{}
    73  }
    74  
    75  // GetLog returns the slice of FakeActions taken by the mounter
    76  func (f *FakeMounter) GetLog() []FakeAction {
    77  	f.mutex.Lock()
    78  	defer f.mutex.Unlock()
    79  
    80  	return f.log
    81  }
    82  
    83  // Mount records the mount event and updates the in-memory mount points for FakeMounter
    84  func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
    85  	return f.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
    86  }
    87  
    88  // Mount records the mount event and updates the in-memory mount points for FakeMounter
    89  // sensitiveOptions to be passed in a separate parameter from the normal
    90  // mount options and ensures the sensitiveOptions are never logged. This
    91  // method should be used by callers that pass sensitive material (like
    92  // passwords) as mount options.
    93  func (f *FakeMounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
    94  	f.mutex.Lock()
    95  	defer f.mutex.Unlock()
    96  
    97  	opts := []string{}
    98  
    99  	for _, option := range options {
   100  		// find 'bind' option
   101  		if option == "bind" {
   102  			// This is a bind-mount. In order to mimic linux behaviour, we must
   103  			// use the original device of the bind-mount as the real source.
   104  			// E.g. when mounted /dev/sda like this:
   105  			//      $ mount /dev/sda /mnt/test
   106  			//      $ mount -o bind /mnt/test /mnt/bound
   107  			// then /proc/mount contains:
   108  			// /dev/sda /mnt/test
   109  			// /dev/sda /mnt/bound
   110  			// (and not /mnt/test /mnt/bound)
   111  			// I.e. we must use /dev/sda as source instead of /mnt/test in the
   112  			// bind mount.
   113  			for _, mnt := range f.MountPoints {
   114  				if source == mnt.Path {
   115  					source = mnt.Device
   116  					break
   117  				}
   118  			}
   119  		}
   120  		// reuse MountPoint.Opts field to mark mount as readonly
   121  		opts = append(opts, option)
   122  	}
   123  
   124  	// If target is a symlink, get its absolute path
   125  	absTarget, err := filepath.EvalSymlinks(target)
   126  	if err != nil {
   127  		absTarget = target
   128  	}
   129  	f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: append(opts, sensitiveOptions...)})
   130  	klog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
   131  	f.log = append(f.log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
   132  	return nil
   133  }
   134  
   135  // Unmount records the unmount event and updates the in-memory mount points for FakeMounter
   136  func (f *FakeMounter) Unmount(target string) error {
   137  	f.mutex.Lock()
   138  	defer f.mutex.Unlock()
   139  
   140  	// If target is a symlink, get its absolute path
   141  	absTarget, err := filepath.EvalSymlinks(target)
   142  	if err != nil {
   143  		absTarget = target
   144  	}
   145  
   146  	newMountpoints := []MountPoint{}
   147  	for _, mp := range f.MountPoints {
   148  		if mp.Path == absTarget {
   149  			if f.UnmountFunc != nil {
   150  				err := f.UnmountFunc(absTarget)
   151  				if err != nil {
   152  					return err
   153  				}
   154  			}
   155  			klog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, absTarget)
   156  			// Don't copy it to newMountpoints
   157  			continue
   158  		}
   159  		newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
   160  	}
   161  	f.MountPoints = newMountpoints
   162  	f.log = append(f.log, FakeAction{Action: FakeActionUnmount, Target: absTarget})
   163  	delete(f.MountCheckErrors, target)
   164  	return nil
   165  }
   166  
   167  // List returns all the in-memory mountpoints for FakeMounter
   168  func (f *FakeMounter) List() ([]MountPoint, error) {
   169  	f.mutex.Lock()
   170  	defer f.mutex.Unlock()
   171  
   172  	return f.MountPoints, nil
   173  }
   174  
   175  // IsLikelyNotMountPoint determines whether a path is a mountpoint by checking
   176  // if the absolute path to file is in the in-memory mountpoints
   177  func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
   178  	f.mutex.Lock()
   179  	defer f.mutex.Unlock()
   180  
   181  	err := f.MountCheckErrors[file]
   182  	if err != nil {
   183  		return false, err
   184  	}
   185  
   186  	_, err = os.Stat(file)
   187  	if err != nil {
   188  		return true, err
   189  	}
   190  
   191  	// If file is a symlink, get its absolute path
   192  	absFile, err := filepath.EvalSymlinks(file)
   193  	if err != nil {
   194  		absFile = file
   195  	}
   196  
   197  	for _, mp := range f.MountPoints {
   198  		if mp.Path == absFile {
   199  			klog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
   200  			return false, nil
   201  		}
   202  	}
   203  	klog.V(5).Infof("isLikelyNotMountPoint for %s: true", file)
   204  	return true, nil
   205  }
   206  
   207  // GetMountRefs finds all mount references to the path, returns a
   208  // list of paths.
   209  func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
   210  	realpath, err := filepath.EvalSymlinks(pathname)
   211  	if err != nil {
   212  		// Ignore error in FakeMounter, because we actually didn't create files.
   213  		realpath = pathname
   214  	}
   215  	return getMountRefsByDev(f, realpath)
   216  }
   217  

View as plain text