Files
zot/pkg/cli/server/config_reloader.go
T
Andrei Aaron dfb5d1df54 fix: make config read/write thread safe (#3432)
* fix: make config read/write thread safe and fix some other similar issues

1. The config config has a lock, and safe methods to update and read the attributes
2. The config has methods to retrieve copies of specific attributes, such as the extyensions config, the auth config, and the authz config.
These are needed, as the config object may mutate in the middle of an auth/authz requests, and we avoid partial configuration being applied for that request.
3. Fix an issue with the monitoring server not stopping when the controller is shut down.
4. Fix an issue with the HTPasswdWatcher not stopping when the background tasks are supposed to finish.
5. Fix some tests using hardcoded ports.

Moved some of the methods which were on the main config to the auth, access control and extension configs

Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
2025-10-18 11:20:58 +03:00

133 lines
3.5 KiB
Go

package server
import (
"errors"
"os"
"os/signal"
"syscall"
"github.com/fsnotify/fsnotify"
"zotregistry.dev/zot/v2/pkg/api"
"zotregistry.dev/zot/v2/pkg/api/config"
"zotregistry.dev/zot/v2/pkg/log"
)
type HotReloader struct {
watcher *fsnotify.Watcher
configPath string
ldapCredentialsPath string
ctlr *api.Controller
logger log.Logger
}
func NewHotReloader(ctlr *api.Controller, filePath, ldapCredentialsPath string) (*HotReloader, error) {
// creates a new file watcher
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
hotReloader := &HotReloader{
watcher: watcher,
configPath: filePath,
ldapCredentialsPath: ldapCredentialsPath,
ctlr: ctlr,
logger: log.NewLogger("info", ""),
}
return hotReloader, nil
}
func signalHandler(ctlr *api.Controller, sigCh chan os.Signal) {
// if signal then shutdown
if sig, ok := <-sigCh; ok {
ctlr.Log.Info().Interface("signal", sig).Msg("received signal")
// gracefully shutdown http server
ctlr.Shutdown() //nolint: contextcheck
}
}
func initShutDownRoutine(ctlr *api.Controller) {
sigCh := make(chan os.Signal, 1)
go signalHandler(ctlr, sigCh)
// block all async signals to this server
signal.Ignore()
// handle SIGINT and SIGHUP.
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
}
func (hr *HotReloader) Start() {
done := make(chan bool)
// run watcher
go func() {
defer hr.watcher.Close()
go func() {
for {
select {
// watch for events
case event := <-hr.watcher.Events:
if event.Op == fsnotify.Write {
hr.logger.Info().Msg("config file changed, trying to reload config")
newConfig := config.New()
err := LoadConfiguration(newConfig, hr.configPath)
if err != nil {
hr.logger.Error().Err(err).Msg("failed to reload config, retry writing it.")
continue
}
authConfig := hr.ctlr.Config.CopyAuthConfig()
if authConfig.IsLdapAuthEnabled() &&
authConfig.LDAP.CredentialsFile != newConfig.HTTP.Auth.LDAP.CredentialsFile {
err = hr.watcher.Remove(authConfig.LDAP.CredentialsFile)
if err != nil && !errors.Is(err, fsnotify.ErrNonExistentWatch) {
hr.logger.Error().Err(err).Msg("failed to remove old watch for the credentials file")
}
err = hr.watcher.Add(newConfig.HTTP.Auth.LDAP.CredentialsFile)
if err != nil {
hr.logger.Panic().Err(err).Str("ldap-credentials-file", newConfig.HTTP.Auth.LDAP.CredentialsFile).
Msg("failed to watch ldap credentials file")
}
}
// stop background tasks gracefully
hr.ctlr.StopBackgroundTasks()
// load new config
hr.ctlr.LoadNewConfig(newConfig)
// start background tasks based on new loaded config
hr.ctlr.StartBackgroundTasks()
}
// watch for errors
case err := <-hr.watcher.Errors:
hr.logger.Panic().Err(err).Str("config", hr.configPath).Msg("fsnotfy error while watching config")
}
}
}()
if err := hr.watcher.Add(hr.configPath); err != nil {
hr.logger.Panic().Err(err).Str("config", hr.configPath).Msg("failed to add config file to fsnotity watcher")
}
if hr.ldapCredentialsPath != "" {
if err := hr.watcher.Add(hr.ldapCredentialsPath); err != nil {
hr.logger.Panic().Err(err).Str("ldap-credentials", hr.ldapCredentialsPath).
Msg("failed to add ldap-credentials to fsnotity watcher")
}
}
<-done
}()
}