zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/cli/server/config_reloader.go (about) 1 package server 2 3 import ( 4 "errors" 5 "os" 6 "os/signal" 7 "syscall" 8 9 "github.com/fsnotify/fsnotify" 10 "github.com/rs/zerolog/log" 11 12 "zotregistry.dev/zot/pkg/api" 13 "zotregistry.dev/zot/pkg/api/config" 14 ) 15 16 type HotReloader struct { 17 watcher *fsnotify.Watcher 18 configPath string 19 ldapCredentialsPath string 20 ctlr *api.Controller 21 } 22 23 func NewHotReloader(ctlr *api.Controller, filePath, ldapCredentialsPath string) (*HotReloader, error) { 24 // creates a new file watcher 25 watcher, err := fsnotify.NewWatcher() 26 if err != nil { 27 return nil, err 28 } 29 30 hotReloader := &HotReloader{ 31 watcher: watcher, 32 configPath: filePath, 33 ldapCredentialsPath: ldapCredentialsPath, 34 ctlr: ctlr, 35 } 36 37 return hotReloader, nil 38 } 39 40 func signalHandler(ctlr *api.Controller, sigCh chan os.Signal) { 41 // if signal then shutdown 42 if sig, ok := <-sigCh; ok { 43 ctlr.Log.Info().Interface("signal", sig).Msg("received signal") 44 45 // gracefully shutdown http server 46 ctlr.Shutdown() //nolint: contextcheck 47 } 48 } 49 50 func initShutDownRoutine(ctlr *api.Controller) { 51 sigCh := make(chan os.Signal, 1) 52 53 go signalHandler(ctlr, sigCh) 54 55 // block all async signals to this server 56 signal.Ignore() 57 58 // handle SIGINT and SIGHUP. 59 signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP) 60 } 61 62 func (hr *HotReloader) Start() { 63 done := make(chan bool) 64 65 // run watcher 66 go func() { 67 defer hr.watcher.Close() 68 69 go func() { 70 for { 71 select { 72 // watch for events 73 case event := <-hr.watcher.Events: 74 if event.Op == fsnotify.Write { 75 log.Info().Msg("config file changed, trying to reload config") 76 77 newConfig := config.New() 78 79 err := LoadConfiguration(newConfig, hr.configPath) 80 if err != nil { 81 log.Error().Err(err).Msg("failed to reload config, retry writing it.") 82 83 continue 84 } 85 86 if hr.ctlr.Config.HTTP.Auth != nil && hr.ctlr.Config.HTTP.Auth.LDAP != nil && 87 hr.ctlr.Config.HTTP.Auth.LDAP.CredentialsFile != newConfig.HTTP.Auth.LDAP.CredentialsFile { 88 err = hr.watcher.Remove(hr.ctlr.Config.HTTP.Auth.LDAP.CredentialsFile) 89 if err != nil && !errors.Is(err, fsnotify.ErrNonExistentWatch) { 90 log.Error().Err(err).Msg("failed to remove old watch for the credentials file") 91 } 92 93 err = hr.watcher.Add(newConfig.HTTP.Auth.LDAP.CredentialsFile) 94 if err != nil { 95 log.Panic().Err(err).Str("ldap-credentials-file", newConfig.HTTP.Auth.LDAP.CredentialsFile). 96 Msg("failed to watch ldap credentials file") 97 } 98 } 99 100 // stop background tasks gracefully 101 hr.ctlr.StopBackgroundTasks() 102 103 // load new config 104 hr.ctlr.LoadNewConfig(newConfig) 105 106 // start background tasks based on new loaded config 107 hr.ctlr.StartBackgroundTasks() 108 } 109 // watch for errors 110 case err := <-hr.watcher.Errors: 111 log.Panic().Err(err).Str("config", hr.configPath).Msg("fsnotfy error while watching config") 112 } 113 } 114 }() 115 116 if err := hr.watcher.Add(hr.configPath); err != nil { 117 log.Panic().Err(err).Str("config", hr.configPath).Msg("failed to add config file to fsnotity watcher") 118 } 119 120 if hr.ldapCredentialsPath != "" { 121 if err := hr.watcher.Add(hr.ldapCredentialsPath); err != nil { 122 log.Panic().Err(err).Str("ldap-credentials", hr.ldapCredentialsPath). 123 Msg("failed to add ldap-credentials to fsnotity watcher") 124 } 125 } 126 127 <-done 128 }() 129 }