Skip to content

Commit fe41ff3

Browse files
alexandre-dauboismholtWeidiDeng
authored
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>
1 parent b7ae39e commit fe41ff3

File tree

2 files changed

+22
-3
lines changed

2 files changed

+22
-3
lines changed

caddy.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ type Config struct {
8181
// associated value.
8282
AppsRaw ModuleMap `json:"apps,omitempty" caddy:"namespace="`
8383

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
8588
storage certmagic.Storage
8689
eventEmitter eventEmitter
8790

@@ -522,6 +525,7 @@ func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error)
522525

523526
// prepare the new config for use
524527
newCfg.apps = make(map[string]App)
528+
newCfg.failedApps = make(map[string]error)
525529

526530
// set up global storage and make it CertMagic's default storage, too
527531
err = func() error {

context.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
393393
return nil, fmt.Errorf("module value cannot be null")
394394
}
395395

396+
var err error
397+
396398
// if this is an app module, keep a reference to it,
397399
// since submodules may need to reference it during
398400
// provisioning (even though the parent app module
@@ -402,12 +404,17 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
402404
// module has been configured for DNS challenges)
403405
if appModule, ok := val.(App); ok {
404406
ctx.cfg.apps[id] = appModule
407+
defer func() {
408+
if err != nil {
409+
ctx.cfg.failedApps[id] = err
410+
}
411+
}()
405412
}
406413

407414
ctx.ancestry = append(ctx.ancestry, val)
408415

409416
if prov, ok := val.(Provisioner); ok {
410-
err := prov.Provision(ctx)
417+
err = prov.Provision(ctx)
411418
if err != nil {
412419
// incomplete provisioning could have left state
413420
// dangling, so make sure it gets cleaned up
@@ -422,7 +429,7 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
422429
}
423430

424431
if validator, ok := val.(Validator); ok {
425-
err := validator.Validate()
432+
err = validator.Validate()
426433
if err != nil {
427434
// since the module was already provisioned, make sure we clean up
428435
if cleanerUpper, ok := val.(CleanerUpper); ok {
@@ -487,6 +494,10 @@ func (ctx Context) loadModuleInline(moduleNameKey, moduleScope string, raw json.
487494
// or stop App modules. The caller is expected to assert to the
488495
// concrete type.
489496
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+
}
490501
if app, ok := ctx.cfg.apps[name]; ok {
491502
return app, nil
492503
}
@@ -511,6 +522,10 @@ func (ctx Context) AppIfConfigured(name string) (any, error) {
511522
if ctx.cfg == nil {
512523
return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
513524
}
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+
}
514529
if app, ok := ctx.cfg.apps[name]; ok {
515530
return app, nil
516531
}

0 commit comments

Comments
 (0)