1
0
Fork 0
mirror of https://github.com/pomerium/pomerium.git synced 2025-06-01 18:33:19 +02:00
pomerium/internal/fileutil/watcher.go
Joe Kralicky fe31799eb5
Fix many instances of contexts and loggers not being propagated ()
This also replaces instances where we manually write "return ctx.Err()"
with "return context.Cause(ctx)" which is functionally identical, but
will also correctly propagate cause errors if present.
2024-10-25 14:50:56 -04:00

105 lines
2.4 KiB
Go

package fileutil
import (
"context"
"sync"
"namespacelabs.dev/go-filenotify"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/signal"
)
// A Watcher watches files for changes.
type Watcher struct {
*signal.Signal
mu sync.Mutex
watching map[string]struct{}
pollingWatcher filenotify.FileWatcher
}
// NewWatcher creates a new Watcher.
func NewWatcher() *Watcher {
return &Watcher{
Signal: signal.New(),
watching: make(map[string]struct{}),
}
}
// Watch updates the watched file paths.
func (watcher *Watcher) Watch(ctx context.Context, filePaths []string) {
watcher.mu.Lock()
defer watcher.mu.Unlock()
watcher.initLocked(ctx)
var add []string
seen := map[string]struct{}{}
for _, filePath := range filePaths {
if _, ok := watcher.watching[filePath]; !ok {
add = append(add, filePath)
}
seen[filePath] = struct{}{}
}
var remove []string
for filePath := range watcher.watching {
if _, ok := seen[filePath]; !ok {
remove = append(remove, filePath)
}
}
for _, filePath := range add {
watcher.watching[filePath] = struct{}{}
if watcher.pollingWatcher != nil {
err := watcher.pollingWatcher.Add(filePath)
if err != nil {
log.Ctx(ctx).Error().Err(err).Str("file", filePath).Msg("fileutil/watcher: failed to add file to polling-based file watcher")
}
}
}
for _, filePath := range remove {
delete(watcher.watching, filePath)
if watcher.pollingWatcher != nil {
err := watcher.pollingWatcher.Remove(filePath)
if err != nil {
log.Ctx(ctx).Error().Err(err).Str("file", filePath).Msg("fileutil/watcher: failed to remove file from polling-based file watcher")
}
}
}
}
func (watcher *Watcher) initLocked(ctx context.Context) {
if watcher.pollingWatcher != nil {
return
}
if watcher.pollingWatcher == nil {
watcher.pollingWatcher = filenotify.NewPollingWatcher(nil)
context.AfterFunc(ctx, func() {
watcher.pollingWatcher.Close()
})
}
errors := watcher.pollingWatcher.Errors()
events := watcher.pollingWatcher.Events()
// log errors
go func() {
for err := range errors {
log.Ctx(ctx).Error().Err(err).Msg("fileutil/watcher: file notification error")
}
}()
// handle events
go func() {
for evt := range events {
log.Ctx(ctx).Info().Str("name", evt.Name).Str("op", evt.Op.String()).Msg("fileutil/watcher: file notification event")
watcher.Broadcast(ctx)
}
}()
}