Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 23:30 UTC
core: Save app provisioning errors with context (#7070)
* fix(provisioning): remove app from apps map when its provision failed * Clean up failed app provisioning with defer * fix(provisioning): record apps that failed to provision with their error * save the error when an app fails to initialize and return this error when this app is requested by a module --------- Co-authored-by: Matt Holt <mholt@users.noreply.github.com> Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
Alexandre Daubois committed
Jul 29, 2025 at 18:31 UTC
fe41ff3c5bbee0aaa44110c110fd6b4fbf052048
2 files changed
+22
-3
caddy.go
+5
-1
@@ -81,7 +81,10 @@ type Config struct {
81
// associated value.
82
AppsRaw ModuleMap `json:"apps,omitempty" caddy:"namespace="`
83
84
- apps map[string]App
84
+ apps map[string]App
85
+
86
+ // failedApps is a map of apps that failed to provision with their underlying error.
87
+ failedApps map[string]error
88
storage certmagic.Storage
89
eventEmitter eventEmitter
90
@@ -522,6 +525,7 @@ func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error)
525
526
// prepare the new config for use
527
newCfg.apps = make(map[string]App)
528
+ newCfg.failedApps = make(map[string]error)
529
530
// set up global storage and make it CertMagic's default storage, too
531
err = func() error {
context.go
+17
-2
@@ -393,6 +393,8 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
393
return nil, fmt.Errorf("module value cannot be null")
394
}
395
396
+ var err error
397
+
398
// if this is an app module, keep a reference to it,
399
// since submodules may need to reference it during
400
// provisioning (even though the parent app module
@@ -402,12 +404,17 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
404
// module has been configured for DNS challenges)
405
if appModule, ok := val.(App); ok {
406
ctx.cfg.apps[id] = appModule
407
+ defer func() {
408
+ if err != nil {
409
+ ctx.cfg.failedApps[id] = err
410
+ }
411
+ }()
412
}
413
414
ctx.ancestry = append(ctx.ancestry, val)
415
416
if prov, ok := val.(Provisioner); ok {
410
- err := prov.Provision(ctx)
417
+ err = prov.Provision(ctx)
418
if err != nil {
419
// incomplete provisioning could have left state
420
// dangling, so make sure it gets cleaned up
@@ -422,7 +429,7 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
429
}
430
431
if validator, ok := val.(Validator); ok {
425
- err := validator.Validate()
432
+ err = validator.Validate()
433
if err != nil {
434
// since the module was already provisioned, make sure we clean up
435
if cleanerUpper, ok := val.(CleanerUpper); ok {
@@ -487,6 +494,10 @@ func (ctx Context) loadModuleInline(moduleNameKey, moduleScope string, raw json.
494
// or stop App modules. The caller is expected to assert to the
495
// concrete type.
496
func (ctx Context) App(name string) (any, error) {
497
+ // if the app failed to load before, return the cached error
498
+ if err, ok := ctx.cfg.failedApps[name]; ok {
499
+ return nil, fmt.Errorf("loading %s app module: %v", name, err)
500
+ }
501
if app, ok := ctx.cfg.apps[name]; ok {
502
return app, nil
503
}
@@ -511,6 +522,10 @@ func (ctx Context) AppIfConfigured(name string) (any, error) {
522
if ctx.cfg == nil {
523
return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
524
}
525
+ // if the app failed to load before, return the cached error
526
+ if err, ok := ctx.cfg.failedApps[name]; ok {
527
+ return nil, fmt.Errorf("loading %s app module: %v", name, err)
528
+ }
529
if app, ok := ctx.cfg.apps[name]; ok {
530
return app, nil
531
}