@marcel91200 / caddy Mirror · read-only / commits / fe41ff3c
Fork 0

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 }