268
newCfg != nil &&
269
newCfg.Admin != nil &&
270
newCfg.Admin.Config != nil &&
271
- newCfg.Admin.Config.LoadRaw != nil {
272
- return fmt.Errorf("recursive config loading detected: pulled configs cannot pull other configs")
271
+ newCfg.Admin.Config.LoadRaw != nil &&
272
+ newCfg.Admin.Config.LoadInterval <= 0 {
273
+ return fmt.Errorf("recursive config loading detected: pulled configs cannot pull other configs without positive load_interval")
274
}
275
276
// run the new config and start all its apps
481
if err != nil {
482
return fmt.Errorf("loading config loader module: %s", err)
483
}
483
- loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx)
484
- if err != nil {
485
- return fmt.Errorf("loading dynamic config from %T: %v", val, err)
486
- }
487
-
488
- // do this in a goroutine so current config can finish being loaded; otherwise deadlock
489
- go func() {
490
- Log().Info("applying dynamically-loaded config", zap.String("loader_module", val.(Module).CaddyModule().ID.Name()))
484
+ runLoadedConfig := func(config []byte) {
485
+ Log().Info("applying dynamically-loaded config", zap.String("loader_module", val.(Module).CaddyModule().ID.Name()), zap.Int("pull_interval", int(cfg.Admin.Config.LoadInterval)))
486
currentCfgMu.Lock()
492
- err := unsyncedDecodeAndRun(loadedConfig, false)
487
+ err := unsyncedDecodeAndRun(config, false)
488
currentCfgMu.Unlock()
489
if err == nil {
490
Log().Info("dynamically-loaded config applied successfully")
491
} else {
492
Log().Error("running dynamically-loaded config failed", zap.Error(err))
493
}
499
- }()
494
+ }
495
+ if cfg.Admin.Config.LoadInterval > 0 {
496
+ go func() {
497
+ select {
498
+ // if LoadInterval is positive, will wait for the interval and then run with new config
499
+ case <-time.After(time.Duration(cfg.Admin.Config.LoadInterval)):
500
+ loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx)
501
+ if err != nil {
502
+ Log().Error("loading dynamic config failed", zap.Error(err))
503
+ return
504
+ }
505
+ runLoadedConfig(loadedConfig)
506
+ case <-ctx.Done():
507
+ return
508
+ }
509
+ }()
510
+ } else {
511
+ // if no LoadInterval is provided, will load config synchronously
512
+ loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx)
513
+ if err != nil {
514
+ return fmt.Errorf("loading dynamic config from %T: %v", val, err)
515
+ }
516
+ // do this in a goroutine so current config can finish being loaded; otherwise deadlock
517
+ go runLoadedConfig(loadedConfig)
518
+ }
519
+
520
}
521
522
return nil