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