bugfix: Move all inotify to filehelper (#279)

This commit is contained in:
James Read 2024-04-19 22:56:21 +01:00 committed by GitHub
parent 362019738d
commit fafacfeafb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 152 deletions

View File

@ -5,10 +5,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
config "github.com/OliveTin/OliveTin/internal/config" config "github.com/OliveTin/OliveTin/internal/config"
"github.com/OliveTin/OliveTin/internal/filehelper"
sv "github.com/OliveTin/OliveTin/internal/stringvariables" sv "github.com/OliveTin/OliveTin/internal/stringvariables"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io/ioutil" "io/ioutil"
"os" "os"
@ -17,7 +16,7 @@ import (
) )
func SetupEntityFileWatchers(cfg *config.Config) { func SetupEntityFileWatchers(cfg *config.Config) {
configDir := filepath.Dir(viper.ConfigFileUsed()) configDir := cfg.GetDir()
configDirVar := filepath.Join(configDir, "var") // for development purposes configDirVar := filepath.Join(configDir, "var") // for development purposes
@ -36,71 +35,9 @@ func SetupEntityFileWatchers(cfg *config.Config) {
}).Debugf("Adding config dir to entity file path") }).Debugf("Adding config dir to entity file path")
} }
go watch(p, ef.Name) filehelper.WatchFileWrite(p, func(filename string) {
loadEntityFile(p, ef.Name)
loadEntityFile(p, ef.Name) })
}
}
func watch(file string, entityname string) {
log.WithFields(log.Fields{
"file": file,
"name": entityname,
}).Infof("Watching entity file")
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Errorf("Could not watch entity file: %v", err)
return
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
processEvent(watcher, file, entityname)
}
}()
err = watcher.Add(file)
if err != nil {
log.WithFields(log.Fields{
"file": file,
}).Errorf("Could not create entity watcher: %v", err)
}
<-done
}
func processEvent(watcher *fsnotify.Watcher, filename string, entityname string) {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
loadEntityFileIfWritten(&event, filename, entityname)
return
case err := <-watcher.Errors:
log.Errorf("Error in fsnotify: %v", err)
return
}
}
func loadEntityFileIfWritten(event *fsnotify.Event, filename string, entityname string) {
if event.Has(fsnotify.Remove) {
log.WithFields(log.Fields{
"file": filename,
}).Warnf("Entity file deleted! Will no longer be able to watch for changes!")
}
if event.Has(fsnotify.Write) {
loadEntityFile(filename, entityname)
} }
} }

View File

@ -305,6 +305,7 @@ func stepRequestAction(req *ExecutionRequest) bool {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle, "actionTitle": req.logEntry.ActionTitle,
"tags": req.Tags,
}).Infof("Action requested") }).Infof("Action requested")
return true return true

View File

@ -6,7 +6,44 @@ import (
"path/filepath" "path/filepath"
) )
func WatchFile(fullpath string, callback func()) { type watchContext struct {
filename string
filedir string
callback func(filename string)
interestedEvent fsnotify.Op
}
func WatchDirectoryCreate(fullpath string, callback func(filename string)) {
watchPath(&watchContext{
filedir: fullpath,
filename: "",
callback: callback,
interestedEvent: fsnotify.Create,
})
}
func WatchDirectoryWrite(fullpath string, callback func(filename string)) {
watchPath(&watchContext{
filedir: fullpath,
filename: "",
callback: callback,
interestedEvent: fsnotify.Write,
})
}
func WatchFileWrite(fullpath string, callback func(filename string)) {
filename := filepath.Base(fullpath)
filedir := filepath.Dir(fullpath)
watchPath(&watchContext{
filedir: filedir,
filename: filename,
callback: callback,
interestedEvent: fsnotify.Write,
})
}
func watchPath(ctx *watchContext) {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
@ -18,16 +55,13 @@ func WatchFile(fullpath string, callback func()) {
done := make(chan bool) done := make(chan bool)
filename := filepath.Base(fullpath)
filedir := filepath.Dir(fullpath)
go func() { go func() {
for { for {
processEvent(filename, watcher, fsnotify.Write, callback) processEvent(ctx, watcher)
} }
}() }()
err = watcher.Add(filedir) err = watcher.Add(ctx.filedir)
if err != nil { if err != nil {
log.Errorf("Could not create watcher: %v", err) log.Errorf("Could not create watcher: %v", err)
@ -36,10 +70,10 @@ func WatchFile(fullpath string, callback func()) {
<-done <-done
} }
func processEvent(filename string, watcher *fsnotify.Watcher, eventType fsnotify.Op, callback func()) { func processEvent(ctx *watchContext, watcher *fsnotify.Watcher) {
select { select {
case event, ok := <-watcher.Events: case event, ok := <-watcher.Events:
if !consumeEvent(ok, filename, &event, callback) { if !consumeEvent(ok, ctx, &event) {
return return
} }
@ -50,25 +84,25 @@ func processEvent(filename string, watcher *fsnotify.Watcher, eventType fsnotify
} }
} }
func consumeEvent(ok bool, filename string, event *fsnotify.Event, callback func()) bool { func consumeEvent(ok bool, ctx *watchContext, event *fsnotify.Event) bool {
if !ok { if !ok {
return false return false
} }
if filepath.Base(event.Name) != filename { if ctx.filename != "" && filepath.Base(event.Name) != ctx.filename {
log.Tracef("fsnotify irreleventa event different file %+v", event) log.Tracef("fsnotify irreleventa event different file %+v", event)
return true return true
} }
consumeWriteEvents(event, callback) consumeRelevantEvents(ctx, event)
return true return true
} }
func consumeWriteEvents(event *fsnotify.Event, callback func()) { func consumeRelevantEvents(ctx *watchContext, event *fsnotify.Event) {
if event.Has(fsnotify.Write) { if event.Has(ctx.interestedEvent) {
log.Debugf("fsnotify write event: %v", event) log.Debugf("fsnotify write event: %v", event)
callback() ctx.callback(event.Name)
} else { } else {
log.Debugf("fsnotify irrelevant event on file %v", event) log.Debugf("fsnotify irrelevant event on file %v", event)
} }

View File

@ -19,22 +19,23 @@ func Schedule(cfg *config.Config, ex *executor.Executor) {
for _, action := range cfg.Actions { for _, action := range cfg.Actions {
if action.ExecOnCalendarFile != "" { if action.ExecOnCalendarFile != "" {
x := func() { x := func(filename string) {
parseCalendarFile(action, cfg, ex) parseCalendarFile(action, cfg, ex, filename)
} }
go filehelper.WatchFile(action.ExecOnCalendarFile, x) go filehelper.WatchFileWrite(action.ExecOnCalendarFile, x)
x() x(action.ExecOnCalendarFile)
} }
} }
} }
func parseCalendarFile(action *config.Action, cfg *config.Config, ex *executor.Executor) { func parseCalendarFile(action *config.Action, cfg *config.Config, ex *executor.Executor, filename string) {
filehelper.Touch(action.ExecOnCalendarFile, "calendar file") filehelper.Touch(action.ExecOnCalendarFile, "calendar file")
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"actionTitle": action.Title, "actionTitle": action.Title,
"filename": filename,
}).Infof("Parsing calendar file") }).Infof("Parsing calendar file")
yfile, err := ioutil.ReadFile(action.ExecOnCalendarFile) yfile, err := ioutil.ReadFile(action.ExecOnCalendarFile)

View File

@ -4,83 +4,37 @@ import (
"github.com/OliveTin/OliveTin/internal/acl" "github.com/OliveTin/OliveTin/internal/acl"
"github.com/OliveTin/OliveTin/internal/config" "github.com/OliveTin/OliveTin/internal/config"
"github.com/OliveTin/OliveTin/internal/executor" "github.com/OliveTin/OliveTin/internal/executor"
"github.com/fsnotify/fsnotify" "github.com/OliveTin/OliveTin/internal/filehelper"
log "github.com/sirupsen/logrus"
) )
func WatchFilesInDirectory(cfg *config.Config, ex *executor.Executor) { func WatchFilesInDirectory(cfg *config.Config, ex *executor.Executor) {
for _, action := range cfg.Actions { for _, action := range cfg.Actions {
for _, dirname := range action.ExecOnFileChangedInDir { for _, dirname := range action.ExecOnFileChangedInDir {
watch(dirname, action, cfg, ex, fsnotify.Write) filehelper.WatchDirectoryWrite(dirname, func(filename string) {
scheduleExec(action, cfg, ex, filename)
})
} }
for _, dirname := range action.ExecOnFileCreatedInDir { for _, dirname := range action.ExecOnFileCreatedInDir {
watch(dirname, action, cfg, ex, fsnotify.Create) filehelper.WatchDirectoryCreate(dirname, func(filename string) {
scheduleExec(action, cfg, ex, filename)
})
} }
} }
} }
func watch(directory string, action *config.Action, cfg *config.Config, ex *executor.Executor, eventType fsnotify.Op) { func scheduleExec(action *config.Action, cfg *config.Config, ex *executor.Executor, filename string) {
log.WithFields(log.Fields{ req := &executor.ExecutionRequest{
"dir": directory, ActionTitle: action.Title,
"eventType": eventType, Cfg: cfg,
}).Infof("Watching dir") Tags: []string{"fileindir"},
Arguments: map[string]string{
watcher, err := fsnotify.NewWatcher() "filename": filename,
},
if err != nil { AuthenticatedUser: &acl.AuthenticatedUser{
log.Errorf("Could not watch for files being created: %v", err) Username: "fileindir",
return },
} }
defer watcher.Close() ex.ExecRequest(req)
done := make(chan bool)
go func() {
for {
processEvent(watcher, action, cfg, ex, eventType)
}
}()
err = watcher.Add("/tmp")
if err != nil {
log.Errorf("Could not create watcher: %v", err)
}
<-done
}
func processEvent(watcher *fsnotify.Watcher, action *config.Action, cfg *config.Config, ex *executor.Executor, eventType fsnotify.Op) {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
checkEvent(&event, action, cfg, ex, eventType)
break
case err := <-watcher.Errors:
log.Errorf("Error in fsnotify: %v", err)
return
}
}
func checkEvent(event *fsnotify.Event, action *config.Action, cfg *config.Config, ex *executor.Executor, eventType fsnotify.Op) {
if event.Has(eventType) {
req := &executor.ExecutionRequest{
ActionTitle: action.Title,
Cfg: cfg,
Tags: []string{"fileindir"},
Arguments: map[string]string{
"filename": event.Name,
},
AuthenticatedUser: &acl.AuthenticatedUser{
Username: "fileindir",
},
}
ex.ExecRequest(req)
}
} }