From c131339c5cda3a541223fde4a714aab55de13b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=B8=85=E9=9B=A8?= Date: Thu, 29 Jul 2021 05:39:08 +0800 Subject: admin: Implement load_interval to pull config on a timer (#4246) * feat: implement a simple timer to pull config mostly referenced to the issue re #4106 * Update admin.go use `caddy.Duration` Co-authored-by: Matt Holt * Update caddy.go Co-authored-by: Matt Holt * Update admin.go Co-authored-by: Francis Lavoie * fix: sync load config when no pull interval provided try not to make break change * fix: change PullInterval to LoadInterval * fix: change pull_interval to load_interval * Update caddy.go Co-authored-by: Matt Holt Co-authored-by: Matt Holt Co-authored-by: Francis Lavoie --- caddy.go | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'caddy.go') diff --git a/caddy.go b/caddy.go index 96dfea5..ba025b1 100644 --- a/caddy.go +++ b/caddy.go @@ -268,8 +268,9 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error { newCfg != nil && newCfg.Admin != nil && newCfg.Admin.Config != nil && - newCfg.Admin.Config.LoadRaw != nil { - return fmt.Errorf("recursive config loading detected: pulled configs cannot pull other configs") + newCfg.Admin.Config.LoadRaw != nil && + newCfg.Admin.Config.LoadInterval <= 0 { + return fmt.Errorf("recursive config loading detected: pulled configs cannot pull other configs without positive load_interval") } // run the new config and start all its apps @@ -480,23 +481,42 @@ func finishSettingUp(ctx Context, cfg *Config) error { if err != nil { return fmt.Errorf("loading config loader module: %s", err) } - loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx) - if err != nil { - return fmt.Errorf("loading dynamic config from %T: %v", val, err) - } - - // do this in a goroutine so current config can finish being loaded; otherwise deadlock - go func() { - Log().Info("applying dynamically-loaded config", zap.String("loader_module", val.(Module).CaddyModule().ID.Name())) + runLoadedConfig := func(config []byte) { + Log().Info("applying dynamically-loaded config", zap.String("loader_module", val.(Module).CaddyModule().ID.Name()), zap.Int("pull_interval", int(cfg.Admin.Config.LoadInterval))) currentCfgMu.Lock() - err := unsyncedDecodeAndRun(loadedConfig, false) + err := unsyncedDecodeAndRun(config, false) currentCfgMu.Unlock() if err == nil { Log().Info("dynamically-loaded config applied successfully") } else { Log().Error("running dynamically-loaded config failed", zap.Error(err)) } - }() + } + if cfg.Admin.Config.LoadInterval > 0 { + go func() { + select { + // if LoadInterval is positive, will wait for the interval and then run with new config + case <-time.After(time.Duration(cfg.Admin.Config.LoadInterval)): + loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx) + if err != nil { + Log().Error("loading dynamic config failed", zap.Error(err)) + return + } + runLoadedConfig(loadedConfig) + case <-ctx.Done(): + return + } + }() + } else { + // if no LoadInterval is provided, will load config synchronously + loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx) + if err != nil { + return fmt.Errorf("loading dynamic config from %T: %v", val, err) + } + // do this in a goroutine so current config can finish being loaded; otherwise deadlock + go runLoadedConfig(loadedConfig) + } + } return nil -- cgit v1.2.3