From ceef70dbc5543436b04a5f70749be18582f35403 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 3 Mar 2022 10:57:55 -0700 Subject: [PATCH] core: Retry dynamic config load if error or no-op (#4603) Also fix ineffectual assignment (unrelated) --- admin.go | 3 ++- caddy.go | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/admin.go b/admin.go index 11efc683b76..8558cee1cca 100644 --- a/admin.go +++ b/admin.go @@ -262,7 +262,7 @@ func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool) admi // provisionAdminRouters provisions all the router modules // in the admin.api namespace that need provisioning. -func (admin AdminConfig) provisionAdminRouters(ctx Context) error { +func (admin *AdminConfig) provisionAdminRouters(ctx Context) error { for _, router := range admin.routers { provisioner, ok := router.(Provisioner) if !ok { @@ -277,6 +277,7 @@ func (admin AdminConfig) provisionAdminRouters(ctx Context) error { // We no longer need the routers once provisioned, allow for GC admin.routers = nil + return nil } diff --git a/caddy.go b/caddy.go index 36439240128..661d0b0c36b 100644 --- a/caddy.go +++ b/caddy.go @@ -504,20 +504,29 @@ func finishSettingUp(ctx Context, cfg *Config) error { if cfg.Admin.Config.LoadDelay > 0 { go func() { - timer := time.NewTimer(time.Duration(cfg.Admin.Config.LoadDelay)) - select { - case <-timer.C: - loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx) - if err != nil { - Log().Error("loading dynamic config failed", zap.Error(err)) - return + // the loop is here only to iterate if there is an error or a no-op config load, + // in which case we simply wait the delay and try again + for { + timer := time.NewTimer(time.Duration(cfg.Admin.Config.LoadDelay)) + select { + case <-timer.C: + loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx) + if err != nil { + Log().Error("failed loading dynamic config; will retry", zap.Error(err)) + continue + } + if loadedConfig == nil { + Log().Info("dynamically-loaded config was nil; will retry") + continue + } + runLoadedConfig(loadedConfig) + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + Log().Info("stopping dynamic config loading") } - runLoadedConfig(loadedConfig) - case <-ctx.Done(): - if !timer.Stop() { - <-timer.C - } - Log().Info("stopping dynamic config loading") + break } }() } else { @@ -534,8 +543,10 @@ func finishSettingUp(ctx Context, cfg *Config) error { return nil } -// ConfigLoader is a type that can load a Caddy config. The -// returned config must be valid Caddy JSON. +// ConfigLoader is a type that can load a Caddy config. If +// the return value is non-nil, it must be valid Caddy JSON; +// if nil or with non-nil error, it is considered to be a +// no-op load and may be retried later. type ConfigLoader interface { LoadConfig(Context) ([]byte, error) }