zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/cli/server/config_reloader.go (about) 1 package server 2 3 import ( 4 "context" 5 "os" 6 "os/signal" 7 "syscall" 8 9 "github.com/fsnotify/fsnotify" 10 "github.com/rs/zerolog/log" 11 12 "zotregistry.io/zot/pkg/api" 13 "zotregistry.io/zot/pkg/api/config" 14 ) 15 16 type HotReloader struct { 17 watcher *fsnotify.Watcher 18 filePath string 19 ctlr *api.Controller 20 } 21 22 func NewHotReloader(ctlr *api.Controller, filePath string) (*HotReloader, error) { 23 // creates a new file watcher 24 watcher, err := fsnotify.NewWatcher() 25 if err != nil { 26 return nil, err 27 } 28 29 hotReloader := &HotReloader{ 30 watcher: watcher, 31 filePath: filePath, 32 ctlr: ctlr, 33 } 34 35 return hotReloader, nil 36 } 37 38 func signalHandler(ctlr *api.Controller, sigCh chan os.Signal, ctx context.Context, cancel context.CancelFunc) { 39 select { 40 // if signal then shutdown 41 case sig := <-sigCh: 42 defer cancel() 43 44 ctlr.Log.Info().Interface("signal", sig).Msg("received signal") 45 46 // gracefully shutdown http server 47 ctlr.Shutdown() //nolint: contextcheck 48 49 close(sigCh) 50 // if reload then return 51 case <-ctx.Done(): 52 return 53 } 54 } 55 56 func initShutDownRoutine(ctlr *api.Controller, ctx context.Context, cancel context.CancelFunc) { 57 sigCh := make(chan os.Signal, 1) 58 59 go signalHandler(ctlr, sigCh, ctx, cancel) 60 61 // block all async signals to this server 62 signal.Ignore() 63 64 // handle SIGINT and SIGHUP. 65 signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP) 66 } 67 68 func (hr *HotReloader) Start() context.Context { 69 reloadCtx, cancelFunc := context.WithCancel(context.Background()) 70 71 done := make(chan bool) 72 73 initShutDownRoutine(hr.ctlr, reloadCtx, cancelFunc) 74 75 // run watcher 76 go func() { 77 defer hr.watcher.Close() 78 79 go func() { 80 for { 81 select { 82 // watch for events 83 case event := <-hr.watcher.Events: 84 if event.Op == fsnotify.Write { 85 log.Info().Msg("config file changed, trying to reload config") 86 87 newConfig := config.New() 88 89 err := LoadConfiguration(newConfig, hr.filePath) 90 if err != nil { 91 log.Error().Err(err).Msg("couldn't reload config, retry writing it.") 92 93 continue 94 } 95 // if valid config then reload 96 cancelFunc() 97 98 // create new context 99 reloadCtx, cancelFunc = context.WithCancel(context.Background()) 100 101 // init shutdown routine 102 initShutDownRoutine(hr.ctlr, reloadCtx, cancelFunc) 103 104 hr.ctlr.LoadNewConfig(reloadCtx, newConfig) 105 } 106 // watch for errors 107 case err := <-hr.watcher.Errors: 108 log.Panic().Err(err).Str("config", hr.filePath).Msg("fsnotfy error while watching config") 109 } 110 } 111 }() 112 113 if err := hr.watcher.Add(hr.filePath); err != nil { 114 log.Panic().Err(err).Str("config", hr.filePath).Msg("error adding config file to FsNotify watcher") 115 } 116 117 <-done 118 }() 119 120 return reloadCtx 121 }