/* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package flexvolume import ( "fmt" "path" "testing" "github.com/fsnotify/fsnotify" "github.com/stretchr/testify/assert" utilfs "k8s.io/kubernetes/pkg/util/filesystem" "k8s.io/kubernetes/pkg/volume" "k8s.io/utils/exec" ) const ( pluginDir = "/flexvolume" driverName = "fake-driver" ) // Probes a driver installed before prober initialization. func TestProberExistingDriverBeforeInit(t *testing.T) { // Arrange driverPath, _, watcher, prober := initTestEnvironment(t) // Act events, err := prober.Probe() // Assert // Probe occurs, 1 plugin should be returned, and 2 watches (pluginDir and all its // current subdirectories) registered. assert.Equal(t, 1, len(events)) assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) assert.Equal(t, pluginDir, watcher.watches[0]) assert.Equal(t, driverPath, watcher.watches[1]) assert.NoError(t, err) // Should no longer probe. // Act events, err = prober.Probe() // Assert assert.Equal(t, 0, len(events)) assert.NoError(t, err) } // Probes newly added drivers after prober is running. func TestProberAddRemoveDriver(t *testing.T) { // Arrange _, fs, watcher, prober := initTestEnvironment(t) prober.Probe() events, err := prober.Probe() assert.Equal(t, 0, len(events)) // Call probe after a file is added. Should return 1 event. // add driver const driverName2 = "fake-driver2" driverPath := path.Join(pluginDir, driverName2) executablePath := path.Join(driverPath, driverName2) installDriver(driverName2, fs) watcher.TriggerEvent(fsnotify.Create, driverPath) watcher.TriggerEvent(fsnotify.Create, executablePath) // Act events, err = prober.Probe() // Assert assert.Equal(t, 1, len(events)) assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) // 1 newly added assert.Equal(t, driverPath, watcher.watches[len(watcher.watches)-1]) // Checks most recent watch assert.NoError(t, err) // Call probe again, should return 0 event. // Act events, err = prober.Probe() // Assert assert.Equal(t, 0, len(events)) assert.NoError(t, err) // Call probe after a non-driver file is added in a subdirectory. should return 1 event. fp := path.Join(driverPath, "dummyfile") fs.Create(fp) watcher.TriggerEvent(fsnotify.Create, fp) // Act events, err = prober.Probe() // Assert assert.Equal(t, 1, len(events)) assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) assert.NoError(t, err) // Call probe again, should return 0 event. // Act events, err = prober.Probe() // Assert assert.Equal(t, 0, len(events)) assert.NoError(t, err) // Call probe after a subdirectory is added in a driver directory. should return 1 event. subdirPath := path.Join(driverPath, "subdir") fs.Create(subdirPath) watcher.TriggerEvent(fsnotify.Create, subdirPath) // Act events, err = prober.Probe() // Assert assert.Equal(t, 1, len(events)) assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) assert.NoError(t, err) // Call probe again, should return 0 event. // Act events, err = prober.Probe() // Assert assert.Equal(t, 0, len(events)) assert.NoError(t, err) // Call probe after a subdirectory is removed in a driver directory. should return 1 event. fs.Remove(subdirPath) watcher.TriggerEvent(fsnotify.Remove, subdirPath) // Act events, err = prober.Probe() // Assert assert.Equal(t, 1, len(events)) assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) assert.NoError(t, err) // Call probe again, should return 0 event. // Act events, err = prober.Probe() // Assert assert.Equal(t, 0, len(events)) assert.NoError(t, err) // Call probe after a driver executable and driver directory is remove. should return 1 event. fs.Remove(executablePath) fs.Remove(driverPath) watcher.TriggerEvent(fsnotify.Remove, executablePath) watcher.TriggerEvent(fsnotify.Remove, driverPath) // Act and Assert: 1 ProbeRemove event events, err = prober.Probe() assert.Equal(t, 1, len(events)) assert.Equal(t, volume.ProbeRemove, events[0].Op) assert.NoError(t, err) // Act and Assert: 0 event events, err = prober.Probe() assert.Equal(t, 0, len(events)) assert.NoError(t, err) } // Tests the behavior when no drivers exist in the plugin directory. func TestEmptyPluginDir(t *testing.T) { // Arrange fs := utilfs.NewFakeFs() watcher := newFakeWatcher() prober := &flexVolumeProber{ pluginDir: pluginDir, watcher: watcher, fs: fs, factory: fakePluginFactory{error: false}, } prober.Init() // Act events, err := prober.Probe() // Assert assert.Equal(t, 0, len(events)) assert.NoError(t, err) } // Issue an event to remove plugindir. New directory should still be watched. func TestRemovePluginDir(t *testing.T) { // Arrange driverPath, fs, watcher, _ := initTestEnvironment(t) fs.RemoveAll(pluginDir) watcher.TriggerEvent(fsnotify.Remove, path.Join(driverPath, driverName)) watcher.TriggerEvent(fsnotify.Remove, driverPath) watcher.TriggerEvent(fsnotify.Remove, pluginDir) // Act: The handler triggered by the above events should have already handled the event appropriately. // Assert assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch. assert.Equal(t, pluginDir, watcher.watches[len(watcher.watches)-1]) } // Issue an event to remove plugindir. New directory should still be watched. func TestNestedDriverDir(t *testing.T) { // Arrange _, fs, watcher, _ := initTestEnvironment(t) // Assert assert.Equal(t, 2, len(watcher.watches)) // 2 from initial setup // test add testDriverName testDriverName := "testDriverName" testDriverPath := path.Join(pluginDir, testDriverName) fs.MkdirAll(testDriverPath, 0666) watcher.TriggerEvent(fsnotify.Create, testDriverPath) // Assert assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch. assert.Equal(t, testDriverPath, watcher.watches[len(watcher.watches)-1]) // test add nested subdir inside testDriverName basePath := testDriverPath for i := 0; i < 10; i++ { subdirName := "subdirName" subdirPath := path.Join(basePath, subdirName) fs.MkdirAll(subdirPath, 0666) watcher.TriggerEvent(fsnotify.Create, subdirPath) // Assert assert.Equal(t, 4+i, len(watcher.watches)) // 3 + newly added assert.Equal(t, subdirPath, watcher.watches[len(watcher.watches)-1]) basePath = subdirPath } } // Issue multiple events and probe multiple times. func TestProberMultipleEvents(t *testing.T) { const iterations = 5 // Arrange _, fs, watcher, prober := initTestEnvironment(t) for i := 0; i < iterations; i++ { newDriver := fmt.Sprintf("multi-event-driver%d", 1) installDriver(newDriver, fs) driverPath := path.Join(pluginDir, newDriver) watcher.TriggerEvent(fsnotify.Create, driverPath) watcher.TriggerEvent(fsnotify.Create, path.Join(driverPath, newDriver)) } // Act events, err := prober.Probe() // Assert assert.Equal(t, 2, len(events)) assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op) assert.Equal(t, volume.ProbeAddOrUpdate, events[1].Op) assert.NoError(t, err) for i := 0; i < iterations-1; i++ { events, err = prober.Probe() assert.Equal(t, 0, len(events)) assert.NoError(t, err) } } func TestProberError(t *testing.T) { fs := utilfs.NewFakeFs() watcher := newFakeWatcher() prober := &flexVolumeProber{ pluginDir: pluginDir, watcher: watcher, fs: fs, factory: fakePluginFactory{error: true}, } installDriver(driverName, fs) prober.Init() _, err := prober.Probe() assert.Error(t, err) } // Installs a mock driver (an empty file) in the mock fs. func installDriver(driverName string, fs utilfs.Filesystem) { driverPath := path.Join(pluginDir, driverName) fs.MkdirAll(driverPath, 0666) fs.Create(path.Join(driverPath, driverName)) } // Initializes mocks, installs a single driver in the mock fs, then initializes prober. func initTestEnvironment(t *testing.T) ( driverPath string, fs utilfs.Filesystem, watcher *fakeWatcher, prober volume.DynamicPluginProber) { fs = utilfs.NewFakeFs() watcher = newFakeWatcher() prober = &flexVolumeProber{ pluginDir: pluginDir, watcher: watcher, fs: fs, factory: fakePluginFactory{error: false}, } driverPath = path.Join(pluginDir, driverName) installDriver(driverName, fs) prober.Init() assert.NotNilf(t, watcher.eventHandler, "Expect watch event handler to be registered after prober init, but is not.") return } // Fake Flexvolume plugin type fakePluginFactory struct { error bool // Indicates whether an error should be returned. } var _ PluginFactory = fakePluginFactory{} func (m fakePluginFactory) NewFlexVolumePlugin(_, driverName string, _ exec.Interface) (volume.VolumePlugin, error) { if m.error { return nil, fmt.Errorf("Flexvolume plugin error") } // Dummy Flexvolume plugin. Prober never interacts with the plugin. return &flexVolumePlugin{driverName: driverName}, nil }