@marcel91200 / caddy Mirror · read-only / commits / a10117f8
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 12, 10:45 UTC

core: Split `run` into a public `ProvisionContext` and a private method (#6378)

* Split `run` into a public `BuildContext` and a private part `BuildContext` can be used to set up a caddy context from a config, but not start any listeners or active components: The returned context has the configured apps provisioned, but otherwise is inert. This is EXPERIMENTAL: Minimally it's missing documentation and the example for how this can be used to run unit tests. * Use the config from the context The config passed into `BuildContext` can be nil, in which case `BuildContext` will just make one up that works. In either case that will end up in the finished context. * Rename `BuildContext` to `ProvisionContext` to better match the function * Hide the `replaceAdminServer` parts The admin server is a global thing, and in the envisioned use case for `ProvisionContext` shouldn't actually exist. Hide this detail in a private `provisionContext` instead, and only expose it publicly with `replaceAdminServer` set to `false`. This should reduce foot-shooting potential further; in addition the documentation comment now clearly spells out that the exact interface and implementation details of `ProvisionContext` are experimental and subject to change.

Andreas Kohn committed Jun 6, 2024 at 22:36 UTC a10117f8bdbfd72fe585b7bb0c4b43ad8f6908bc
1 file changed +62 -43
caddy.go
+62 -43
@@ -397,6 +397,58 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error {
397 // will want to use Run instead, which also
398 // updates the config's raw state.
399 func run(newCfg *Config, start bool) (Context, error) {
400 + ctx, err := provisionContext(newCfg, start)
401 + if err != nil {
402 + return ctx, err
403 + }
404 +
405 + if !start {
406 + return ctx, nil
407 + }
408 +
409 + // Provision any admin routers which may need to access
410 + // some of the other apps at runtime
411 + err = ctx.cfg.Admin.provisionAdminRouters(ctx)
412 + if err != nil {
413 + return ctx, err
414 + }
415 +
416 + // Start
417 + err = func() error {
418 + started := make([]string, 0, len(ctx.cfg.apps))
419 + for name, a := range ctx.cfg.apps {
420 + err := a.Start()
421 + if err != nil {
422 + // an app failed to start, so we need to stop
423 + // all other apps that were already started
424 + for _, otherAppName := range started {
425 + err2 := ctx.cfg.apps[otherAppName].Stop()
426 + if err2 != nil {
427 + err = fmt.Errorf("%v; additionally, aborting app %s: %v",
428 + err, otherAppName, err2)
429 + }
430 + }
431 + return fmt.Errorf("%s app module: start: %v", name, err)
432 + }
433 + started = append(started, name)
434 + }
435 + return nil
436 + }()
437 + if err != nil {
438 + return ctx, err
439 + }
440 +
441 + // now that the user's config is running, finish setting up anything else,
442 + // such as remote admin endpoint, config loader, etc.
443 + return ctx, finishSettingUp(ctx, ctx.cfg)
444 +}
445 +
446 +// provisionContext creates a new context from the given configuration and provisions
447 +// storage and apps.
448 +// If `newCfg` is nil a new empty configuration will be created.
449 +// If `replaceAdminServer` is true any currently active admin server will be replaced
450 +// with a new admin server based on the provided configuration.
451 +func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error) {
452 // because we will need to roll back any state
453 // modifications if this function errors, we
454 // keep a single error value and scope all
@@ -444,7 +496,7 @@ func run(newCfg *Config, start bool) (Context, error) {
496 }
497
498 // start the admin endpoint (and stop any prior one)
447 - if start {
499 + if replaceAdminServer {
500 err = replaceLocalAdminServer(newCfg)
501 if err != nil {
502 return ctx, fmt.Errorf("starting caddy administration endpoint: %v", err)
@@ -491,49 +543,16 @@ func run(newCfg *Config, start bool) (Context, error) {
543 }
544 return nil
545 }()
494 - if err != nil {
495 - return ctx, err
496 - }
497 -
498 - if !start {
499 - return ctx, nil
500 - }
501 -
502 - // Provision any admin routers which may need to access
503 - // some of the other apps at runtime
504 - err = newCfg.Admin.provisionAdminRouters(ctx)
505 - if err != nil {
506 - return ctx, err
507 - }
508 -
509 - // Start
510 - err = func() error {
511 - started := make([]string, 0, len(newCfg.apps))
512 - for name, a := range newCfg.apps {
513 - err := a.Start()
514 - if err != nil {
515 - // an app failed to start, so we need to stop
516 - // all other apps that were already started
517 - for _, otherAppName := range started {
518 - err2 := newCfg.apps[otherAppName].Stop()
519 - if err2 != nil {
520 - err = fmt.Errorf("%v; additionally, aborting app %s: %v",
521 - err, otherAppName, err2)
522 - }
523 - }
524 - return fmt.Errorf("%s app module: start: %v", name, err)
525 - }
526 - started = append(started, name)
527 - }
528 - return nil
529 - }()
530 - if err != nil {
531 - return ctx, err
532 - }
546 + return ctx, err
547 +}
548
534 - // now that the user's config is running, finish setting up anything else,
535 - // such as remote admin endpoint, config loader, etc.
536 - return ctx, finishSettingUp(ctx, newCfg)
549 +// ProvisionContext creates a new context from the configuration and provisions storage
550 +// and app modules.
551 +// The function is intended for testing and advanced use cases only, typically `Run` should be
552 +// use to ensure a fully functional caddy instance.
553 +// EXPERIMENTAL: While this is public the interface and implementation details of this function may change.
554 +func ProvisionContext(newCfg *Config) (Context, error) {
555 + return provisionContext(newCfg, false)
556 }
557
558 // finishSettingUp should be run after all apps have successfully started.