github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/fs/fake/fakefs.go (about)

     1  /*
     2  Copyright 2019 Mirantis
     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 fake
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"testing"
    27  
    28  	"github.com/Mirantis/virtlet/pkg/fs"
    29  	testutils "github.com/Mirantis/virtlet/pkg/utils/testing"
    30  )
    31  
    32  const pathMarker = "/__fs__/"
    33  
    34  func fixPath(s string) string {
    35  	p := strings.Index(s, pathMarker)
    36  	if p < 0 {
    37  		return s
    38  	}
    39  	return "/" + s[p+len(pathMarker):]
    40  }
    41  
    42  type fakeDelimitedReader struct {
    43  	rec      testutils.Recorder
    44  	fileData string
    45  }
    46  
    47  var _ fs.DelimitedReader = &fakeDelimitedReader{}
    48  
    49  // ReadString implements ReadString method of utils.FileReader interface
    50  func (fr *fakeDelimitedReader) ReadString(delim byte) (line string, err error) {
    51  	lines := strings.SplitN(fr.fileData, string(delim), 1)
    52  	line = lines[0]
    53  	if len(lines) > 1 {
    54  		fr.fileData = lines[1]
    55  	} else {
    56  		err = io.EOF
    57  	}
    58  	fr.rec.Rec("ReadString", line)
    59  	return
    60  }
    61  
    62  // Close implements Close method of utils.FileReader interface
    63  func (fr *fakeDelimitedReader) Close() error {
    64  	return nil
    65  }
    66  
    67  // FakeFileSystem is a fake implementation of FileSystem interface
    68  // that uses a Recorder to record the operations performed on it.
    69  type FakeFileSystem struct {
    70  	t              *testing.T
    71  	rec            testutils.Recorder
    72  	mountParentDir string
    73  	files          map[string]string
    74  }
    75  
    76  var _ fs.FileSystem = &FakeFileSystem{}
    77  
    78  // NewFakeFileSystem creates a new instance of FakeFileSystem using
    79  // the provided recorder and a directory that should be parent for all
    80  // the fake mountpoints. It also takes map with fake files that will
    81  // be accessible with GetDelimitedReader (besides those written with
    82  // WriteFile).
    83  func NewFakeFileSystem(t *testing.T, rec testutils.Recorder, mountParentDir string, files map[string]string) *FakeFileSystem {
    84  	return &FakeFileSystem{t: t, rec: rec, mountParentDir: mountParentDir, files: files}
    85  }
    86  
    87  func (fs *FakeFileSystem) validateMountPath(target string) {
    88  	if fs.mountParentDir == "" || filepath.Dir(target) != filepath.Clean(fs.mountParentDir) {
    89  		fs.t.Fatalf("bad path encountered by the fs: %q (mountParentDir %q)", target, fs.mountParentDir)
    90  	}
    91  }
    92  
    93  // Mount implements the Mount method of FileSystem interface.
    94  func (fs *FakeFileSystem) Mount(source string, target string, fstype string, bind bool) error {
    95  	fs.validateMountPath(target)
    96  	fs.rec.Rec("Mount", []interface{}{fixPath(source), fixPath(target), fstype, bind})
    97  
    98  	// We want to check directory contents both before & after mount,
    99  	// see comment in FlexVolumeDriver.mount() in flexvolume.go.
   100  	// So we move the original contents to .shadowed subdir.
   101  	shadowedPath := filepath.Join(target, ".shadowed")
   102  	if err := os.Mkdir(shadowedPath, 0755); err != nil {
   103  		fs.t.Fatalf("os.Mkdir(): %v", err)
   104  	}
   105  
   106  	pathsToShadow, err := filepath.Glob(filepath.Join(target, "*"))
   107  	if err != nil {
   108  		fs.t.Fatalf("filepath.Glob(): %v", err)
   109  	}
   110  	for _, pathToShadow := range pathsToShadow {
   111  		filename := filepath.Base(pathToShadow)
   112  		if filename == ".shadowed" {
   113  			continue
   114  		}
   115  		if err := os.Rename(pathToShadow, filepath.Join(shadowedPath, filename)); err != nil {
   116  			fs.t.Fatalf("os.Rename(): %v", err)
   117  		}
   118  	}
   119  	return nil
   120  }
   121  
   122  // Unmount implements the Unmount method of FileSystem interface.
   123  func (fs *FakeFileSystem) Unmount(target string, detach bool) error {
   124  	// we make sure that path is under our tmpdir before wiping it
   125  	fs.validateMountPath(target)
   126  	fs.rec.Rec("Unmount", []interface{}{fixPath(target), detach})
   127  
   128  	paths, err := filepath.Glob(filepath.Join(target, "*"))
   129  	if err != nil {
   130  		fs.t.Fatalf("filepath.Glob(): %v", err)
   131  	}
   132  	for _, path := range paths {
   133  		if filepath.Base(path) != ".shadowed" {
   134  			continue
   135  		}
   136  		if err := os.RemoveAll(path); err != nil {
   137  			fs.t.Fatalf("os.RemoveAll(): %v", err)
   138  		}
   139  	}
   140  
   141  	// We don't clean up '.shadowed' dir here because flexvolume driver
   142  	// recursively removes the whole dir tree anyway.
   143  	return nil
   144  }
   145  
   146  // IsPathAnNs implements the IsPathAnNs method of FileSystem interface.
   147  func (fs *FakeFileSystem) IsPathAnNs(path string) bool {
   148  	return false
   149  }
   150  
   151  // ChownForEmulator implements ChownForEmulator method of FileSystem interface.
   152  func (fs *FakeFileSystem) ChownForEmulator(filePath string, recursive bool) error {
   153  	fs.rec.Rec("ChownForEmulator", []interface{}{fixPath(filePath), recursive})
   154  	return nil
   155  }
   156  
   157  // GetDelimitedReader implements the FileReader method of FileSystem interface.
   158  func (fs *FakeFileSystem) GetDelimitedReader(path string) (fs.DelimitedReader, error) {
   159  	data, ok := fs.files[path]
   160  	if !ok {
   161  		fs.rec.Rec("GetDelimitedReader", fmt.Sprintf("undefined path %q", path))
   162  		return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("file not found")}
   163  	}
   164  	fs.rec.Rec("GetDelimitedReader", path)
   165  	return &fakeDelimitedReader{rec: fs.rec, fileData: data}, nil
   166  }
   167  
   168  // WriteFile implements the WriteFile method of FilesManipulator interface.
   169  func (fs *FakeFileSystem) WriteFile(path string, data []byte, perm os.FileMode) error {
   170  	fs.rec.Rec("WriteFile", []interface{}{path, string(data)})
   171  	fs.files[path] = string(data)
   172  	return nil
   173  }