@marcel91200 / caddy Mirror · read-only / commits / 46c5db92
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, 17:00 UTC

core: OnExit hooks (#6128)

* core: OnExit callbacks * core: Process-global OnExit callbacks

Matt Holt committed Mar 1, 2024 at 09:57 UTC 46c5db92da8ff971db26a3f11282fba5594a7f28
2 files changed +41 -2
caddy.go
+28
@@ -715,6 +715,7 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
715 logger.Warn("exiting; byeee!! 👋")
716
717 exitCode := ExitCodeSuccess
718 + lastContext := ActiveContext()
719
720 // stop all apps
721 if err := Stop(); err != nil {
@@ -736,6 +737,16 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
737 }
738 }
739
740 + // execute any process-exit callbacks
741 + for _, exitFunc := range lastContext.exitFuncs {
742 + exitFunc(ctx)
743 + }
744 + exitFuncsMu.Lock()
745 + for _, exitFunc := range exitFuncs {
746 + exitFunc(ctx)
747 + }
748 + exitFuncsMu.Unlock()
749 +
750 // shut down admin endpoint(s) in goroutines so that
751 // if this function was called from an admin handler,
752 // it has a chance to return gracefully
@@ -774,6 +785,23 @@ var exiting = new(int32) // accessed atomically
785 // EXPERIMENTAL API: subject to change or removal.
786 func Exiting() bool { return atomic.LoadInt32(exiting) == 1 }
787
788 +// OnExit registers a callback to invoke during process exit.
789 +// This registration is PROCESS-GLOBAL, meaning that each
790 +// function should only be registered once forever, NOT once
791 +// per config load (etc).
792 +//
793 +// EXPERIMENTAL API: subject to change or removal.
794 +func OnExit(f func(context.Context)) {
795 + exitFuncsMu.Lock()
796 + exitFuncs = append(exitFuncs, f)
797 + exitFuncsMu.Unlock()
798 +}
799 +
800 +var (
801 + exitFuncs []func(context.Context)
802 + exitFuncsMu sync.Mutex
803 +)
804 +
805 // Duration can be an integer or a string. An integer is
806 // interpreted as nanoseconds. If a string, it is a Go
807 // time.Duration value such as `300ms`, `1.5h`, or `2h45m`;
context.go
+13 -2
@@ -44,8 +44,9 @@ type Context struct {
44
45 moduleInstances map[string][]Module
46 cfg *Config
47 - cleanupFuncs []func()
47 ancestry []Module
48 + cleanupFuncs []func() // invoked at every config unload
49 + exitFuncs []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL)
50 }
51
52 // NewContext provides a new context derived from the given
@@ -86,7 +87,8 @@ func (ctx *Context) OnCancel(f func()) {
87 ctx.cleanupFuncs = append(ctx.cleanupFuncs, f)
88 }
89
89 -// Filesystems returns a ref to the FilesystemMap
90 +// Filesystems returns a ref to the FilesystemMap.
91 +// EXPERIMENTAL: This API is subject to change.
92 func (ctx *Context) Filesystems() FileSystems {
93 // if no config is loaded, we use a default filesystemmap, which includes the osfs
94 if ctx.cfg == nil {
@@ -95,6 +97,15 @@ func (ctx *Context) Filesystems() FileSystems {
97 return ctx.cfg.filesystems
98 }
99
100 +// OnExit executes f when the process exits gracefully.
101 +// The function is only executed if the process is gracefully
102 +// shut down while this context is active.
103 +//
104 +// EXPERIMENTAL API: subject to change or removal.
105 +func (ctx *Context) OnExit(f func(context.Context)) {
106 + ctx.exitFuncs = append(ctx.exitFuncs, f)
107 +}
108 +
109 // LoadModule loads the Caddy module(s) from the specified field of the parent struct
110 // pointer and returns the loaded module(s). The struct pointer and its field name as
111 // a string are necessary so that reflection can be used to read the struct tag on the