1/* 2Copyright 2017 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package flexvolume 18 19import ( 20 "fmt" 21 "path/filepath" 22 "strings" 23 "testing" 24 25 "github.com/fsnotify/fsnotify" 26 "github.com/stretchr/testify/assert" 27 utilfs "k8s.io/kubernetes/pkg/util/filesystem" 28 "k8s.io/kubernetes/pkg/volume" 29 "k8s.io/utils/exec" 30) 31 32const ( 33 pluginDir = "/flexvolume" 34 driverName = "fake-driver" 35) 36 37func assertPathSuffix(t *testing.T, dir1 string, dir2 string) { 38 assert.True(t, strings.HasSuffix(dir2, dir1)) 39} 40 41// Probes a driver installed before prober initialization. 42func TestProberExistingDriverBeforeInit(t *testing.T) { 43 // Arrange 44 driverPath, _, watcher, prober := initTestEnvironment(t) 45 46 // Act 47 events, err := prober.Probe() 48 49 // Assert 50 // Probe occurs, 1 plugin should be returned, and 2 watches (pluginDir and all its 51 // current subdirectories) registered. 52 assert.Equal(t, 1, len(events)) 53 assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) 54 assertPathSuffix(t, pluginDir, watcher.watches[0]) 55 assertPathSuffix(t, driverPath, watcher.watches[1]) 56 assert.NoError(t, err) 57 58 // Should no longer probe. 59 60 // Act 61 events, err = prober.Probe() 62 // Assert 63 assert.Equal(t, 0, len(events)) 64 assert.NoError(t, err) 65} 66 67// Probes newly added drivers after prober is running. 68func TestProberAddRemoveDriver(t *testing.T) { 69 // Arrange 70 _, fs, watcher, prober := initTestEnvironment(t) 71 prober.Probe() 72 events, err := prober.Probe() 73 assert.NoError(t, err) 74 assert.Equal(t, 0, len(events)) 75 76 // Call probe after a file is added. Should return 1 event. 77 78 // add driver 79 const driverName2 = "fake-driver2" 80 driverPath := filepath.Join(pluginDir, driverName2) 81 executablePath := filepath.Join(driverPath, driverName2) 82 installDriver(driverName2, fs) 83 watcher.TriggerEvent(fsnotify.Create, driverPath) 84 watcher.TriggerEvent(fsnotify.Create, executablePath) 85 86 // Act 87 events, err = prober.Probe() 88 89 // Assert 90 assert.Equal(t, 1, len(events)) 91 assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) // 1 newly added 92 assertPathSuffix(t, driverPath, watcher.watches[len(watcher.watches)-1]) // Checks most recent watch 93 assert.NoError(t, err) 94 95 // Call probe again, should return 0 event. 96 97 // Act 98 events, err = prober.Probe() 99 // Assert 100 assert.Equal(t, 0, len(events)) 101 assert.NoError(t, err) 102 103 // Call probe after a non-driver file is added in a subdirectory. should return 1 event. 104 fp := filepath.Join(driverPath, "dummyfile") 105 fs.Create(fp) 106 watcher.TriggerEvent(fsnotify.Create, fp) 107 108 // Act 109 events, err = prober.Probe() 110 111 // Assert 112 assert.Equal(t, 1, len(events)) 113 assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) 114 assert.NoError(t, err) 115 116 // Call probe again, should return 0 event. 117 // Act 118 events, err = prober.Probe() 119 // Assert 120 assert.Equal(t, 0, len(events)) 121 assert.NoError(t, err) 122 123 // Call probe after a subdirectory is added in a driver directory. should return 1 event. 124 subdirPath := filepath.Join(driverPath, "subdir") 125 fs.Create(subdirPath) 126 watcher.TriggerEvent(fsnotify.Create, subdirPath) 127 128 // Act 129 events, err = prober.Probe() 130 131 // Assert 132 assert.Equal(t, 1, len(events)) 133 assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) 134 assert.NoError(t, err) 135 136 // Call probe again, should return 0 event. 137 // Act 138 events, err = prober.Probe() 139 // Assert 140 assert.Equal(t, 0, len(events)) 141 assert.NoError(t, err) 142 143 // Call probe after a subdirectory is removed in a driver directory. should return 1 event. 144 fs.Remove(subdirPath) 145 watcher.TriggerEvent(fsnotify.Remove, subdirPath) 146 147 // Act 148 events, err = prober.Probe() 149 150 // Assert 151 assert.Equal(t, 1, len(events)) 152 assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) 153 assert.NoError(t, err) 154 155 // Call probe again, should return 0 event. 156 // Act 157 events, err = prober.Probe() 158 // Assert 159 assert.Equal(t, 0, len(events)) 160 assert.NoError(t, err) 161 162 // Call probe after a driver executable and driver directory is remove. should return 1 event. 163 fs.Remove(executablePath) 164 fs.Remove(driverPath) 165 watcher.TriggerEvent(fsnotify.Remove, executablePath) 166 watcher.TriggerEvent(fsnotify.Remove, driverPath) 167 // Act and Assert: 1 ProbeRemove event 168 events, err = prober.Probe() 169 assert.Equal(t, 1, len(events)) 170 assert.Equal(t, volume.ProbeRemove, events[0].Op) 171 assert.NoError(t, err) 172 173 // Act and Assert: 0 event 174 events, err = prober.Probe() 175 assert.Equal(t, 0, len(events)) 176 assert.NoError(t, err) 177} 178 179// Tests the behavior when no drivers exist in the plugin directory. 180func TestEmptyPluginDir(t *testing.T) { 181 // Arrange 182 fs := utilfs.NewTempFs() 183 watcher := newFakeWatcher() 184 prober := &flexVolumeProber{ 185 pluginDir: pluginDir, 186 watcher: watcher, 187 fs: fs, 188 factory: fakePluginFactory{error: false}, 189 } 190 prober.Init() 191 192 // Act 193 events, err := prober.Probe() 194 195 // Assert 196 assert.Equal(t, 0, len(events)) 197 assert.NoError(t, err) 198} 199 200// Issue an event to remove plugindir. New directory should still be watched. 201func TestRemovePluginDir(t *testing.T) { 202 // Arrange 203 driverPath, fs, watcher, _ := initTestEnvironment(t) 204 fs.RemoveAll(pluginDir) 205 watcher.TriggerEvent(fsnotify.Remove, filepath.Join(driverPath, driverName)) 206 watcher.TriggerEvent(fsnotify.Remove, driverPath) 207 watcher.TriggerEvent(fsnotify.Remove, pluginDir) 208 209 // Act: The handler triggered by the above events should have already handled the event appropriately. 210 211 // Assert 212 assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch. 213 assertPathSuffix(t, pluginDir, watcher.watches[len(watcher.watches)-1]) 214} 215 216// Issue an event to remove plugindir. New directory should still be watched. 217func TestNestedDriverDir(t *testing.T) { 218 // Arrange 219 _, fs, watcher, _ := initTestEnvironment(t) 220 // Assert 221 assert.Equal(t, 2, len(watcher.watches)) // 2 from initial setup 222 223 // test add testDriverName 224 testDriverName := "testDriverName" 225 testDriverPath := filepath.Join(pluginDir, testDriverName) 226 fs.MkdirAll(testDriverPath, 0777) 227 watcher.TriggerEvent(fsnotify.Create, testDriverPath) 228 // Assert 229 assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch. 230 assertPathSuffix(t, testDriverPath, watcher.watches[len(watcher.watches)-1]) 231 232 // test add nested subdir inside testDriverName 233 basePath := testDriverPath 234 for i := 0; i < 10; i++ { 235 subdirName := "subdirName" 236 subdirPath := filepath.Join(basePath, subdirName) 237 fs.MkdirAll(subdirPath, 0777) 238 watcher.TriggerEvent(fsnotify.Create, subdirPath) 239 // Assert 240 assert.Equal(t, 4+i, len(watcher.watches)) // 3 + newly added 241 assertPathSuffix(t, subdirPath, watcher.watches[len(watcher.watches)-1]) 242 basePath = subdirPath 243 } 244} 245 246// Issue multiple events and probe multiple times. 247func TestProberMultipleEvents(t *testing.T) { 248 const iterations = 5 249 250 // Arrange 251 _, fs, watcher, prober := initTestEnvironment(t) 252 for i := 0; i < iterations; i++ { 253 newDriver := fmt.Sprintf("multi-event-driver%d", 1) 254 installDriver(newDriver, fs) 255 driverPath := filepath.Join(pluginDir, newDriver) 256 watcher.TriggerEvent(fsnotify.Create, driverPath) 257 watcher.TriggerEvent(fsnotify.Create, filepath.Join(driverPath, newDriver)) 258 } 259 260 // Act 261 events, err := prober.Probe() 262 263 // Assert 264 assert.Equal(t, 2, len(events)) 265 assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) 266 assert.Equal(t, volume.ProbeAddOrUpdate, events[1].Op) 267 assert.NoError(t, err) 268 for i := 0; i < iterations-1; i++ { 269 events, err = prober.Probe() 270 assert.Equal(t, 0, len(events)) 271 assert.NoError(t, err) 272 } 273} 274 275func TestProberError(t *testing.T) { 276 fs := utilfs.NewTempFs() 277 watcher := newFakeWatcher() 278 prober := &flexVolumeProber{ 279 pluginDir: pluginDir, 280 watcher: watcher, 281 fs: fs, 282 factory: fakePluginFactory{error: true}, 283 } 284 installDriver(driverName, fs) 285 prober.Init() 286 287 _, err := prober.Probe() 288 assert.Error(t, err) 289} 290 291// Installs a mock driver (an empty file) in the mock fs. 292func installDriver(driverName string, fs utilfs.Filesystem) { 293 driverPath := filepath.Join(pluginDir, driverName) 294 fs.MkdirAll(driverPath, 0777) 295 fs.Create(filepath.Join(driverPath, driverName)) 296} 297 298// Initializes mocks, installs a single driver in the mock fs, then initializes prober. 299func initTestEnvironment(t *testing.T) ( 300 driverPath string, 301 fs utilfs.Filesystem, 302 watcher *fakeWatcher, 303 prober volume.DynamicPluginProber) { 304 fs = utilfs.NewTempFs() 305 watcher = newFakeWatcher() 306 prober = &flexVolumeProber{ 307 pluginDir: pluginDir, 308 watcher: watcher, 309 fs: fs, 310 factory: fakePluginFactory{error: false}, 311 } 312 driverPath = filepath.Join(pluginDir, driverName) 313 installDriver(driverName, fs) 314 prober.Init() 315 316 assert.NotNilf(t, watcher.eventHandler, 317 "Expect watch event handler to be registered after prober init, but is not.") 318 return 319} 320 321// Fake Flexvolume plugin 322type fakePluginFactory struct { 323 error bool // Indicates whether an error should be returned. 324} 325 326var _ PluginFactory = fakePluginFactory{} 327 328func (m fakePluginFactory) NewFlexVolumePlugin(_, driverName string, _ exec.Interface) (volume.VolumePlugin, error) { 329 if m.error { 330 return nil, fmt.Errorf("Flexvolume plugin error") 331 } 332 // Dummy Flexvolume plugin. Prober never interacts with the plugin. 333 return &flexVolumePlugin{driverName: driverName}, nil 334} 335