mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-28 02:37:11 +00:00
Merge pull request #432 from marquiz/devel/config-watch
nfd-worker: use fsnotify for watching for config file changes
This commit is contained in:
commit
a8c7135eda
2 changed files with 95 additions and 21 deletions
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.15
|
|||
|
||||
require (
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/klauspost/cpuid/v2 v2.0.2
|
||||
github.com/onsi/ginkgo v1.11.0
|
||||
|
|
|
@ -24,10 +24,12 @@ import (
|
|||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
@ -178,6 +180,40 @@ func NewNfdWorker(args Args) (NfdWorker, error) {
|
|||
return nfd, nil
|
||||
}
|
||||
|
||||
func addConfigWatch(path string) (*fsnotify.Watcher, map[string]struct{}, error) {
|
||||
paths := make(map[string]struct{})
|
||||
|
||||
// Create watcher
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return w, paths, fmt.Errorf("failed to create fsnotify watcher: %v", err)
|
||||
}
|
||||
|
||||
// Add watches for all directory components so that we catch e.g. renames
|
||||
// upper in the tree
|
||||
added := false
|
||||
for p := path; ; p = filepath.Dir(p) {
|
||||
|
||||
if err := w.Add(p); err != nil {
|
||||
stdoutLogger.Printf("failed to add fsnotify watch for %q: %v", p, err)
|
||||
} else {
|
||||
stdoutLogger.Printf("added fsnotify watch %q", p)
|
||||
added = true
|
||||
}
|
||||
|
||||
paths[p] = struct{}{}
|
||||
if filepath.Dir(p) == p {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !added {
|
||||
// Want to be sure that we watch something
|
||||
return w, paths, fmt.Errorf("failed to add any watch")
|
||||
}
|
||||
return w, paths, nil
|
||||
}
|
||||
|
||||
// Run NfdWorker client. Returns if a fatal error is encountered, or, after
|
||||
// one request if OneShot is set to 'true' in the worker args.
|
||||
func (w *nfdWorker) Run() error {
|
||||
|
@ -191,34 +227,71 @@ func (w *nfdWorker) Run() error {
|
|||
}
|
||||
defer w.disconnect()
|
||||
|
||||
// Create watcher for config file and read initial configuration
|
||||
configFilePath := filepath.Clean(w.args.ConfigFile)
|
||||
configWatch, paths, err := addConfigWatch(configFilePath)
|
||||
if err != nil {
|
||||
return (err)
|
||||
}
|
||||
w.configure(configFilePath, w.args.Options)
|
||||
|
||||
labelTrigger := time.After(0)
|
||||
var configTrigger <-chan time.Time
|
||||
for {
|
||||
// Parse and apply configuration
|
||||
w.configure(w.args.ConfigFile, w.args.Options)
|
||||
select {
|
||||
case <-labelTrigger:
|
||||
// Get the set of feature labels.
|
||||
labels := createFeatureLabels(w.sources, w.labelWhiteList)
|
||||
|
||||
// Get the set of feature labels.
|
||||
labels := createFeatureLabels(w.sources, w.labelWhiteList)
|
||||
|
||||
// Update the node with the feature labels.
|
||||
if w.client != nil {
|
||||
err := advertiseFeatureLabels(w.client, labels)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to advertise labels: %s", err.Error())
|
||||
// Update the node with the feature labels.
|
||||
if w.client != nil {
|
||||
err := advertiseFeatureLabels(w.client, labels)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to advertise labels: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w.args.Oneshot {
|
||||
break
|
||||
}
|
||||
if w.args.Oneshot {
|
||||
return nil
|
||||
}
|
||||
|
||||
if w.args.SleepInterval > 0 {
|
||||
time.Sleep(w.args.SleepInterval)
|
||||
} else {
|
||||
w.disconnect()
|
||||
// Sleep forever
|
||||
select {}
|
||||
if w.args.SleepInterval > 0 {
|
||||
labelTrigger = time.After(w.args.SleepInterval)
|
||||
}
|
||||
|
||||
case e := <-configWatch.Events:
|
||||
name := filepath.Clean(e.Name)
|
||||
|
||||
// If any of our paths (directories or the file itself) change
|
||||
if _, ok := paths[name]; ok {
|
||||
stdoutLogger.Printf("fsnotify event in %q detected, reconfiguring fsnotify and reloading configuration", name)
|
||||
|
||||
// Blindly remove existing watch and add a new one
|
||||
if err := configWatch.Close(); err != nil {
|
||||
stderrLogger.Printf("WARNING: failed to close fsnotify watcher: %v", err)
|
||||
}
|
||||
configWatch, paths, err = addConfigWatch(configFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rate limiter. In certain filesystem operations we get
|
||||
// numerous events in quick succession and we only want one
|
||||
// config re-load
|
||||
configTrigger = time.After(time.Second)
|
||||
}
|
||||
|
||||
case e := <-configWatch.Errors:
|
||||
stderrLogger.Printf("ERROR: config file watcher error: %v", e)
|
||||
|
||||
case <-configTrigger:
|
||||
w.configure(configFilePath, w.args.Options)
|
||||
|
||||
// Always re-label after a re-config event. This way the new config
|
||||
// comes into effect even if the sleep interval is long (or infinite)
|
||||
labelTrigger = time.After(0)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connect creates a client connection to the NFD master
|
||||
|
|
Loading…
Add table
Reference in a new issue