1/* 2 Copyright The containerd 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 17package testing 18 19import ( 20 "os" 21 "sync" 22 23 containerdmount "github.com/containerd/containerd/mount" 24 25 osInterface "github.com/containerd/containerd/pkg/os" 26) 27 28// CalledDetail is the struct contains called function name and arguments. 29type CalledDetail struct { 30 // Name of the function called. 31 Name string 32 // Arguments of the function called. 33 Arguments []interface{} 34} 35 36// FakeOS mocks out certain OS calls to avoid perturbing the filesystem 37// If a member of the form `*Fn` is set, that function will be called in place 38// of the real call. 39type FakeOS struct { 40 sync.Mutex 41 MkdirAllFn func(string, os.FileMode) error 42 RemoveAllFn func(string) error 43 StatFn func(string) (os.FileInfo, error) 44 ResolveSymbolicLinkFn func(string) (string, error) 45 FollowSymlinkInScopeFn func(string, string) (string, error) 46 CopyFileFn func(string, string, os.FileMode) error 47 WriteFileFn func(string, []byte, os.FileMode) error 48 MountFn func(source string, target string, fstype string, flags uintptr, data string) error 49 UnmountFn func(target string) error 50 LookupMountFn func(path string) (containerdmount.Info, error) 51 HostnameFn func() (string, error) 52 calls []CalledDetail 53 errors map[string]error 54} 55 56var _ osInterface.OS = &FakeOS{} 57 58// getError get error for call 59func (f *FakeOS) getError(op string) error { 60 f.Lock() 61 defer f.Unlock() 62 err, ok := f.errors[op] 63 if ok { 64 delete(f.errors, op) 65 return err 66 } 67 return nil 68} 69 70// InjectError inject error for call 71func (f *FakeOS) InjectError(fn string, err error) { 72 f.Lock() 73 defer f.Unlock() 74 f.errors[fn] = err 75} 76 77// InjectErrors inject errors for calls 78func (f *FakeOS) InjectErrors(errs map[string]error) { 79 f.Lock() 80 defer f.Unlock() 81 for fn, err := range errs { 82 f.errors[fn] = err 83 } 84} 85 86// ClearErrors clear errors for call 87func (f *FakeOS) ClearErrors() { 88 f.Lock() 89 defer f.Unlock() 90 f.errors = make(map[string]error) 91} 92 93func (f *FakeOS) appendCalls(name string, args ...interface{}) { 94 f.Lock() 95 defer f.Unlock() 96 f.calls = append(f.calls, CalledDetail{Name: name, Arguments: args}) 97} 98 99// GetCalls get detail of calls. 100func (f *FakeOS) GetCalls() []CalledDetail { 101 f.Lock() 102 defer f.Unlock() 103 return append([]CalledDetail{}, f.calls...) 104} 105 106// NewFakeOS creates a FakeOS. 107func NewFakeOS() *FakeOS { 108 return &FakeOS{ 109 errors: make(map[string]error), 110 } 111} 112 113// MkdirAll is a fake call that invokes MkdirAllFn or just returns nil. 114func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error { 115 f.appendCalls("MkdirAll", path, perm) 116 if err := f.getError("MkdirAll"); err != nil { 117 return err 118 } 119 120 if f.MkdirAllFn != nil { 121 return f.MkdirAllFn(path, perm) 122 } 123 return nil 124} 125 126// RemoveAll is a fake call that invokes RemoveAllFn or just returns nil. 127func (f *FakeOS) RemoveAll(path string) error { 128 f.appendCalls("RemoveAll", path) 129 if err := f.getError("RemoveAll"); err != nil { 130 return err 131 } 132 133 if f.RemoveAllFn != nil { 134 return f.RemoveAllFn(path) 135 } 136 return nil 137} 138 139// Stat is a fake call that invokes StatFn or just return nil. 140func (f *FakeOS) Stat(name string) (os.FileInfo, error) { 141 f.appendCalls("Stat", name) 142 if err := f.getError("Stat"); err != nil { 143 return nil, err 144 } 145 146 if f.StatFn != nil { 147 return f.StatFn(name) 148 } 149 return nil, nil 150} 151 152// ResolveSymbolicLink is a fake call that invokes ResolveSymbolicLinkFn or returns its input 153func (f *FakeOS) ResolveSymbolicLink(path string) (string, error) { 154 f.appendCalls("ResolveSymbolicLink", path) 155 if err := f.getError("ResolveSymbolicLink"); err != nil { 156 return "", err 157 } 158 159 if f.ResolveSymbolicLinkFn != nil { 160 return f.ResolveSymbolicLinkFn(path) 161 } 162 return path, nil 163} 164 165// FollowSymlinkInScope is a fake call that invokes FollowSymlinkInScope or returns its input 166func (f *FakeOS) FollowSymlinkInScope(path, scope string) (string, error) { 167 f.appendCalls("FollowSymlinkInScope", path, scope) 168 if err := f.getError("FollowSymlinkInScope"); err != nil { 169 return "", err 170 } 171 172 if f.FollowSymlinkInScopeFn != nil { 173 return f.FollowSymlinkInScopeFn(path, scope) 174 } 175 return path, nil 176} 177 178// CopyFile is a fake call that invokes CopyFileFn or just return nil. 179func (f *FakeOS) CopyFile(src, dest string, perm os.FileMode) error { 180 f.appendCalls("CopyFile", src, dest, perm) 181 if err := f.getError("CopyFile"); err != nil { 182 return err 183 } 184 185 if f.CopyFileFn != nil { 186 return f.CopyFileFn(src, dest, perm) 187 } 188 return nil 189} 190 191// WriteFile is a fake call that invokes WriteFileFn or just return nil. 192func (f *FakeOS) WriteFile(filename string, data []byte, perm os.FileMode) error { 193 f.appendCalls("WriteFile", filename, data, perm) 194 if err := f.getError("WriteFile"); err != nil { 195 return err 196 } 197 198 if f.WriteFileFn != nil { 199 return f.WriteFileFn(filename, data, perm) 200 } 201 return nil 202} 203 204// Mount is a fake call that invokes MountFn or just return nil. 205func (f *FakeOS) Mount(source string, target string, fstype string, flags uintptr, data string) error { 206 f.appendCalls("Mount", source, target, fstype, flags, data) 207 if err := f.getError("Mount"); err != nil { 208 return err 209 } 210 211 if f.MountFn != nil { 212 return f.MountFn(source, target, fstype, flags, data) 213 } 214 return nil 215} 216 217// Unmount is a fake call that invokes UnmountFn or just return nil. 218func (f *FakeOS) Unmount(target string) error { 219 f.appendCalls("Unmount", target) 220 if err := f.getError("Unmount"); err != nil { 221 return err 222 } 223 224 if f.UnmountFn != nil { 225 return f.UnmountFn(target) 226 } 227 return nil 228} 229 230// LookupMount is a fake call that invokes LookupMountFn or just return nil. 231func (f *FakeOS) LookupMount(path string) (containerdmount.Info, error) { 232 f.appendCalls("LookupMount", path) 233 if err := f.getError("LookupMount"); err != nil { 234 return containerdmount.Info{}, err 235 } 236 237 if f.LookupMountFn != nil { 238 return f.LookupMountFn(path) 239 } 240 return containerdmount.Info{}, nil 241} 242 243// Hostname is a fake call that invokes HostnameFn or just return nil. 244func (f *FakeOS) Hostname() (string, error) { 245 f.appendCalls("Hostname") 246 if err := f.getError("Hostname"); err != nil { 247 return "", err 248 } 249 250 if f.HostnameFn != nil { 251 return f.HostnameFn() 252 } 253 return "", nil 254} 255