diff --git a/cmd/OliveTin/main.go b/cmd/OliveTin/main.go index c6ca646..91cbf96 100644 --- a/cmd/OliveTin/main.go +++ b/cmd/OliveTin/main.go @@ -9,6 +9,7 @@ import ( grpcapi "github.com/OliveTin/OliveTin/internal/grpcapi" "github.com/OliveTin/OliveTin/internal/installationinfo" "github.com/OliveTin/OliveTin/internal/oncron" + "github.com/OliveTin/OliveTin/internal/onfileindir" "github.com/OliveTin/OliveTin/internal/onstartup" updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck" "github.com/OliveTin/OliveTin/internal/websocket" @@ -115,6 +116,7 @@ func main() { go onstartup.Execute(cfg, executor) go oncron.Schedule(cfg, executor) + go onfileindir.WatchFilesInDirectory(cfg, executor) go updatecheck.StartUpdateChecker(version, commit, cfg, configDir) diff --git a/internal/config/config.go b/internal/config/config.go index fdb6e98..3f9348c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,18 +3,20 @@ package config // Action represents the core functionality of OliveTin - commands that show up // as buttons in the UI. type Action struct { - ID string - Title string - Icon string - Shell string - CSS map[string]string `mapstructure:"omitempty"` - Timeout int - Acls []string - ExecOnStartup bool - ExecOnCron []string - MaxConcurrent int - Arguments []ActionArgument - PopupOnStart bool + ID string + Title string + Icon string + Shell string + CSS map[string]string `mapstructure:"omitempty"` + Timeout int + Acls []string + ExecOnStartup bool + ExecOnCron []string + ExecOnFileCreatedInDir []string + ExecOnFileChangedInDir []string + MaxConcurrent int + Arguments []ActionArgument + PopupOnStart bool } // ActionArgument objects appear on Actions. diff --git a/internal/onfileindir/fileindir.go b/internal/onfileindir/fileindir.go new file mode 100644 index 0000000..b3eb836 --- /dev/null +++ b/internal/onfileindir/fileindir.go @@ -0,0 +1,85 @@ +package onfileindir + +import ( + "github.com/OliveTin/OliveTin/internal/acl" + "github.com/OliveTin/OliveTin/internal/config" + "github.com/OliveTin/OliveTin/internal/executor" + "github.com/fsnotify/fsnotify" + log "github.com/sirupsen/logrus" +) + +func WatchFilesInDirectory(cfg *config.Config, ex *executor.Executor) { + for _, action := range cfg.Actions { + for _, dirname := range action.ExecOnFileChangedInDir { + watch(dirname, action, cfg, ex, fsnotify.Write) + } + + for _, dirname := range action.ExecOnFileCreatedInDir { + watch(dirname, action, cfg, ex, fsnotify.Create) + } + } +} + +func watch(directory string, action config.Action, cfg *config.Config, ex *executor.Executor, eventType fsnotify.Op) { + log.WithFields(log.Fields{ + "dir": directory, + "eventType": eventType, + }).Infof("Watching dir") + + watcher, err := fsnotify.NewWatcher() + + if err != nil { + log.Errorf("Could not watch for files being created: %v", err) + return + } + + defer watcher.Close() + + 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) + 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{ + ActionName: action.Title, + Cfg: cfg, + Tags: []string{"fileindir"}, + Arguments: map[string]string{ + "filename": event.Name, + }, + AuthenticatedUser: &acl.AuthenticatedUser{ + Username: "fileindir", + }, + } + + ex.ExecRequest(req) + } +}