diff --git a/pkg/nfd-worker/nfd-worker.go b/pkg/nfd-worker/nfd-worker.go index f54a901ec5..4c5c809076 100644 --- a/pkg/nfd-worker/nfd-worker.go +++ b/pkg/nfd-worker/nfd-worker.go @@ -121,6 +121,7 @@ type nfdWorker struct { k8sClient k8sclient.Interface nfdClient nfdclient.Interface stop chan struct{} // channel for signaling stop + sourceEvent chan struct{} // channel for events from soures featureSources []source.FeatureSource labelSources []source.LabelSource ownerReference []metav1.OwnerReference @@ -304,6 +305,12 @@ func (w *nfdWorker) Run() error { labelTrigger.Reset(w.config.Core.SleepInterval.Duration) defer labelTrigger.Stop() + w.sourceEvent = make(chan struct{}) + eventSources := source.GetAllEventSources() + for _, s := range eventSources { + s.SetChannel(w.sourceEvent) + } + httpMux := http.NewServeMux() // Register to metrics server @@ -341,6 +348,12 @@ func (w *nfdWorker) Run() error { return err } + case <-w.sourceEvent: + err = w.runFeatureDiscovery() + if err != nil { + return err + } + case <-w.stop: klog.InfoS("shutting down nfd-worker") return nil diff --git a/source/local/local.go b/source/local/local.go index ebad5a1cca..a9fc49f645 100644 --- a/source/local/local.go +++ b/source/local/local.go @@ -26,6 +26,7 @@ import ( "k8s.io/klog/v2" + "github.com/fsnotify/fsnotify" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1" "sigs.k8s.io/node-feature-discovery/pkg/utils" "sigs.k8s.io/node-feature-discovery/source" @@ -65,10 +66,11 @@ var ( featureFilesDir = "/etc/kubernetes/node-feature-discovery/features.d/" ) -// localSource implements the FeatureSource and LabelSource interfaces. +// localSource implements the FeatureSource, LabelSource, EventSource interfaces. type localSource struct { - features *nfdv1alpha1.Features - config *Config + features *nfdv1alpha1.Features + config *Config + fsWatcher *fsnotify.Watcher } type Config struct { @@ -87,6 +89,7 @@ var ( _ source.FeatureSource = &src _ source.LabelSource = &src _ source.ConfigurableSource = &src + _ source.EventSource = &src ) // Name method of the LabelSource interface @@ -318,6 +321,47 @@ func getFileContent(fileName string) ([][]byte, error) { return lines, nil } +func (s *localSource) runNotifier(ch chan struct{}) { + for { + select { + case event := <-s.fsWatcher.Events: + if event.Op&fsnotify.Create == fsnotify.Create || event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Remove == fsnotify.Remove || event.Op&fsnotify.Rename == fsnotify.Rename || event.Op&fsnotify.Chmod == fsnotify.Chmod { + klog.InfoS("fsnotify event", event) + ch <- struct{}{} + } + case err := <-s.fsWatcher.Errors: + klog.ErrorS(err, "failed to to watch features.d changes") + } + } +} + +// SetChannel method of the EventSource Interface +func (s *localSource) SetChannel(ch chan struct{}) error { + info, err := os.Stat(featureFilesDir) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } + + if info != nil && info.IsDir() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + + err = watcher.Add(featureFilesDir) + if err != nil { + return fmt.Errorf("unable to access %v: %w", featureFilesDir, err) + } + s.fsWatcher = watcher + } + + go s.runNotifier(ch) + + return nil +} + func init() { source.Register(&src) } diff --git a/source/source.go b/source/source.go index 24b27b4ffd..a3f33ca471 100644 --- a/source/source.go +++ b/source/source.go @@ -77,6 +77,14 @@ type SupplementalSource interface { DisableByDefault() bool } +// EventSource is an interface for a source that can send events +type EventSource interface { + Source + + // SetChannel sets the channel + SetChannel(chan struct{}) error +} + // FeatureLabelValue represents the value of one feature label type FeatureLabelValue interface{} @@ -155,6 +163,17 @@ func GetAllConfigurableSources() map[string]ConfigurableSource { return all } +// GetAllEventSources returns all registered event sources +func GetAllEventSources() map[string]EventSource { + all := make(map[string]EventSource) + for k, v := range sources { + if s, ok := v.(EventSource); ok { + all[k] = s + } + } + return all +} + // GetAllFeatures returns a combined set of all features from all feature // sources. func GetAllFeatures() *nfdv1alpha1.Features {