Change:
When kubelet is running if you create two volume plugin directories here: /usr/libexec/kubernetes/kubelet-plugins/volume/exec /usr/libexec/kubernetes/kubelet-plugins/volume/exec/cifs /usr/libexec/kubernetes/kubelet-plugins/volume/exec/nodeagent~uds You will see these expected logs: Now if you copy over the cifs plugin file to cifs directory and then copy the uds plugin the errors go away. The pods using the uds volume will function but any pods attempting to use the cifs volume will fail with the following error from kubelet: desiredStateOfWorld. err=failed to get Plugin from volumeSpec for volume "pv" err=no volume plugin matched Restarting kubelet fixes this problem as then both plugins are present and properly . A functional volume plugin should be to kubelet even if another volume plugin is missing or invalid. Fixing the invalid volume plugin currently only adds the one that was invalid and fixed and other valid plugins that were previously present are skipped. Start kubelet service on a node. Create two directories for two flex volume plugins in the /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ directory. Eg. nodeagent~uds and cifs Kubelet logs will now start complaining that the plugins are missing. Add the files to the plugin folders and then start a pod using the plugin. Notice that only the last plugin file copied is available to be used by pods. The first plugin will generate this error if used by a volume "err=no volume plugin matched" Performing a touch on the folder of the missing plugin will cause kubelet to properly index it. <details</details<detailsLocal kube cluster </details<details</details<details</details<details</details<details</details
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/sig k8s-infra
/sig storage /remove-sig k8s-infra

Discussion:
) const ( <del> pluginDir  = "/flexvolume" driverName = "fake-driver" </del> <ins> pluginDir       = "/flexvolume" driverName      = "fake-driver" errorDriverName = "error-driver" </ins> ) func assertPathSuffix(t *testing.T, dir1 string, dir2 string) {

pluginDir: pluginDir, watcher:   watcher, fs:        fs, <del> factory:   fakePluginFactory{error: false}, </del> <ins> factory:   fakePluginFactory{}, </ins> } prober.Init()

pluginDir: pluginDir, watcher:   watcher, fs:        fs, <del> factory:   fakePluginFactory{error: true}, </del> <ins> factory:   fakePluginFactory{errorDriver: driverName}, </ins> } installDriver(driverName, fs) prober.Init()

assert.Error(t, err) } <ins> 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) } </ins> // Installs a mock driver (an empty file) in the mock fs. func installDriver(driverName string, fs utilfs.Filesystem) { driverPath := filepath.Join(pluginDir, driverName)

pluginDir: pluginDir, watcher:   watcher, fs:        fs, <del> factory:   fakePluginFactory{error: false}, </del> <ins> factory:   fakePluginFactory{}, </ins> } driverPath = filepath.Join(pluginDir, driverName) installDriver(driverName, fs)

// Fake Flexvolume plugin type fakePluginFactory struct { <del> error bool // Indicates whether an error should be returned. </del> <ins> errorDriver string // the name of the driver in error </ins> } var _ PluginFactory = fakePluginFactory{} func (m fakePluginFactory) NewFlexVolumePlugin(_, driverName string, _ exec.Interface) (volume.VolumePlugin, error) { <del> if m.error { </del> <ins> if driverName == m.errorDriver { </ins> return nil, fmt.Errorf("Flexvolume plugin error") } // Dummy Flexvolume plugin. Prober never interacts with the plugin.

type DynamicPluginProber interface { Init() error <del> // If an error occurs, events are undefined. </del> <ins> // aggregates events for successful drivers and errors for failed drivers </ins> Probe() (events []ProbeEvent, err error) }

// If it is, initialize all probed plugins and replace the cache with them. func (pm *VolumePluginMgr) refreshProbedPlugins() { events, err := pm.prober.Probe() if err != nil { klog.ErrorS(err, "Error dynamically probing plugins") <del> return // Use cached plugins upon failure. </del> } <ins> // because the probe function can return a list of valid plugins // even when an error is present we still must add the plugins // or they will be skipped because each event only fires once </ins> for _, event := range events { if event.Op == ProbeAddOrUpdate { if err := pm.initProbedPlugin(event.Plugin); err != nil {