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 --- admin.go | 6 ++++++ caddy.go | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/admin.go b/admin.go index 8eef916..ff5bc71 100644 --- a/admin.go +++ b/admin.go @@ -109,6 +109,12 @@ type ConfigSettings struct { // // EXPERIMENTAL: Subject to change. LoadRaw json.RawMessage `json:"load,omitempty" caddy:"namespace=caddy.config_loaders inline_key=module"` + + // The interval to pull config. With a non-zero value, will pull config + // from config loader (eg. a http loader) with given interval. + // + // EXPERIMENTAL: Subject to change. + LoadInterval Duration `json:"load_interval,omitempty"` } // IdentityConfig configures management of this server's identity. An identity 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