mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
7642e5af98
* fix(scheduler): data race when pushing new tasks the problem here is that scheduler can be closed in two ways: - canceling the context given as argument to scheduler.RunScheduler() - running scheduler.Shutdown() because of this shutdown can trigger a data race between calling scheduler.inShutdown() and actually pushing tasks into the pool workers solved that by keeping a quit channel and listening on both quit channel and ctx.Done() and closing the worker chan and scheduler afterwards. Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> * refactor(scheduler): refactor into a single shutdown before this we could stop scheduler either by closing the context provided to RunScheduler(ctx) or by running Shutdown(). simplify things by getting rid of the external context in RunScheduler(). keep an internal context in the scheduler itself and pass it down to all tasks. Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> --------- Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
110 lines
2.3 KiB
Go
110 lines
2.3 KiB
Go
package server
|
|
|
|
import (
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"zotregistry.io/zot/pkg/api"
|
|
"zotregistry.io/zot/pkg/api/config"
|
|
)
|
|
|
|
type HotReloader struct {
|
|
watcher *fsnotify.Watcher
|
|
filePath string
|
|
ctlr *api.Controller
|
|
}
|
|
|
|
func NewHotReloader(ctlr *api.Controller, filePath string) (*HotReloader, error) {
|
|
// creates a new file watcher
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hotReloader := &HotReloader{
|
|
watcher: watcher,
|
|
filePath: filePath,
|
|
ctlr: ctlr,
|
|
}
|
|
|
|
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
|
|
|
|
close(sigCh)
|
|
}
|
|
}
|
|
|
|
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)
|
|
|
|
initShutDownRoutine(hr.ctlr)
|
|
|
|
// 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 {
|
|
log.Info().Msg("config file changed, trying to reload config")
|
|
|
|
newConfig := config.New()
|
|
|
|
err := LoadConfiguration(newConfig, hr.filePath)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("failed to reload config, retry writing it.")
|
|
|
|
continue
|
|
}
|
|
|
|
// 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:
|
|
log.Panic().Err(err).Str("config", hr.filePath).Msg("fsnotfy error while watching config")
|
|
}
|
|
}
|
|
}()
|
|
|
|
if err := hr.watcher.Add(hr.filePath); err != nil {
|
|
log.Panic().Err(err).Str("config", hr.filePath).Msg("failed to add config file to fsnotity watcher")
|
|
}
|
|
|
|
<-done
|
|
}()
|
|
}
|