kubernetes/pkg/volume/flexvolume/probe_test.go
Claudiu Belu 38092cb458 unittests: Fixes unit tests for Windows (part 2)
Currently, there are some unit tests that are failing on Windows due to
various reasons:

- volume mounting is a bit different on Windows: Mount will create the
  parent dirs and mklink at the volume path later (otherwise mklink will
  raise an error).
- os.Chmod is not working as intended on Windows.
- path.Dir() will always return "." on Windows, and filepath.Dir()
  should be used instead (which works correctly).
- on Windows, you can't typically run binaries without extensions. If
  the file C:\\foo.bat exists, we can still run C:\\foo because Windows
  will append one of the supported file extensions ($env:PATHEXT) to it
  and run it.
- Windows file permissions do not work the same way as the Linux ones.
- /tmp directory being used, which might not exist on Windows. Instead,
  the OS-specific Temp directory should be used.

Fixes a few other issues:

- rbd.go: Return error in a case in which an error is encountered. This
  will prevent "rbd: failed to setup" and "rbd: successfully setup" log
  messages to be logged at the same time.
2022-08-01 18:56:32 +03:00

377 lines
10 KiB
Go

/*
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/filepath"
goruntime "runtime"
"strings"
"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"
errorDriverName = "error-driver"
)
func assertPathSuffix(t *testing.T, dir1 string, dir2 string) {
assert.True(t, strings.HasSuffix(dir2, dir1))
}
// 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)
plugDir := pluginDir
if goruntime.GOOS == "windows" {
plugDir = "\\flexvolume"
}
assertPathSuffix(t, plugDir, watcher.watches[0])
assertPathSuffix(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.NoError(t, err)
assert.Equal(t, 0, len(events))
// Call probe after a file is added. Should return 1 event.
// add driver
const driverName2 = "fake-driver2"
driverPath := filepath.Join(pluginDir, driverName2)
executablePath := filepath.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
assertPathSuffix(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 := filepath.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 := filepath.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.NewTempFs()
watcher := newFakeWatcher()
prober := &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{},
}
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)
err := fs.RemoveAll(pluginDir)
assert.NoError(t, err)
watcher.TriggerEvent(fsnotify.Remove, filepath.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.
plugDir := pluginDir
if goruntime.GOOS == "windows" {
plugDir = "\\flexvolume"
}
assertPathSuffix(t, plugDir, 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 := filepath.Join(pluginDir, testDriverName)
fs.MkdirAll(testDriverPath, 0777)
watcher.TriggerEvent(fsnotify.Create, testDriverPath)
// Assert
assert.Equal(t, 3, len(watcher.watches)) // 2 from initial setup, 1 from new watch.
assertPathSuffix(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 := filepath.Join(basePath, subdirName)
fs.MkdirAll(subdirPath, 0777)
watcher.TriggerEvent(fsnotify.Create, subdirPath)
// Assert
assert.Equal(t, 4+i, len(watcher.watches)) // 3 + newly added
assertPathSuffix(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 := filepath.Join(pluginDir, newDriver)
watcher.TriggerEvent(fsnotify.Create, driverPath)
watcher.TriggerEvent(fsnotify.Create, filepath.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.NewTempFs()
watcher := newFakeWatcher()
prober := &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{errorDriver: driverName},
}
installDriver(driverName, fs)
prober.Init()
_, err := prober.Probe()
assert.Error(t, err)
}
func TestProberSuccessAndError(t *testing.T) {
// Arrange
fs := utilfs.NewTempFs()
watcher := newFakeWatcher()
prober := &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{errorDriver: errorDriverName},
}
installDriver(driverName, fs)
prober.Init()
installDriver(errorDriverName, fs)
driverPath := filepath.Join(pluginDir, errorDriverName)
watcher.TriggerEvent(fsnotify.Create, filepath.Join(driverPath, errorDriverName))
// Act
events, err := prober.Probe()
// Assert
assert.Equal(t, 1, len(events))
assert.Equal(t, volume.ProbeAddOrUpdate, events[0].Op)
assert.Equal(t, driverName, events[0].PluginName)
assert.Error(t, err)
}
// Installs a mock driver (an empty file) in the mock fs.
func installDriver(driverName string, fs utilfs.Filesystem) {
driverPath := filepath.Join(pluginDir, driverName)
fs.MkdirAll(driverPath, 0777)
// We need to close the file, otherwise we won't be able to remove it.
f, _ := fs.Create(filepath.Join(driverPath, driverName))
f.Close()
}
// 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.NewTempFs()
watcher = newFakeWatcher()
prober = &flexVolumeProber{
pluginDir: pluginDir,
watcher: watcher,
fs: fs,
factory: fakePluginFactory{},
}
driverPath = filepath.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 {
errorDriver string // the name of the driver in error
}
var _ PluginFactory = fakePluginFactory{}
func (m fakePluginFactory) NewFlexVolumePlugin(_, driverName string, _ exec.Interface) (volume.VolumePlugin, error) {
if driverName == m.errorDriver {
return nil, fmt.Errorf("Flexvolume plugin error")
}
// Dummy Flexvolume plugin. Prober never interacts with the plugin.
return &flexVolumePlugin{driverName: driverName}, nil
}