Commit 5cced604 authored by Matthew Holt's avatar Matthew Holt

startup: Only run commands at first startup

We had to hack some special support into the server and caddy packages for this. There are some middlewares which should only execute commands when the original parent process first starts up. For example, someone using the startup directive to start a backend service would not expect the command to be executed every time the config was reloaded or changed - only once when they first started the original caddy process.

This commit adds FirstStartup to the virtualhost config
parent 76ec785e
......@@ -71,6 +71,10 @@ var (
// index in the list of inherited file descriptors. This
// variable is not safe for concurrent access.
loadedGob caddyfileGob
// startedBefore should be set to true if caddy has been
// started at least once.
startedBefore bool
)
const (
......@@ -128,6 +132,7 @@ func Start(cdyfile Input) (err error) {
if err != nil {
return err
}
startedBefore = true
// Close remaining file descriptors we may have inherited that we don't need
if IsRestart() {
......@@ -203,6 +208,18 @@ func startServers(groupings bindingGroup) error {
wg.Add(1)
go func(s *server.Server, ln server.ListenerFile) {
defer wg.Done()
// run startup functions that should only execute when
// the original parent process is starting.
if !IsRestart() && !startedBefore {
err := s.RunFirstStartupFuncs()
if err != nil {
errChan <- err
return
}
}
// start the server
if ln != nil {
errChan <- s.Serve(ln)
} else {
......
......@@ -10,7 +10,7 @@ import (
// Startup registers a startup callback to execute during server start.
func Startup(c *Controller) (middleware.Middleware, error) {
return nil, registerCallback(c, &c.Startup)
return nil, registerCallback(c, &c.FirstStartup)
}
// Shutdown registers a shutdown callback to execute during process exit.
......
......@@ -45,7 +45,7 @@ func TestStartup(t *testing.T) {
if err != nil {
t.Errorf("Expected no errors, got: %v", err)
}
err = c.Startup[0]()
err = c.FirstStartup[0]()
if err != nil && !test.shouldExecutionErr {
t.Errorf("Test %d recieved an error of:\n%v", i, err)
}
......
......@@ -26,11 +26,21 @@ type Config struct {
// Middleware stack; map of path scope to middleware -- TODO: Support path scope?
Middleware map[string][]middleware.Middleware
// Functions (or methods) to execute at server start; these
// are executed before any parts of the server are configured,
// and the functions are blocking
// Startup is a list of functions (or methods) to execute at
// server startup and restart; these are executed before any
// parts of the server are configured, and the functions are
// blocking. These are good for setting up middlewares and
// starting goroutines.
Startup []func() error
// FirstStartup is like Startup but these functions only execute
// during the initial startup, not on subsequent restarts.
//
// (Note: The server does not ever run these on its own; it is up
// to the calling application to do so, and do so only once, as the
// server itself has no notion whether it's a restart or not.)
FirstStartup []func() error
// Functions (or methods) to execute when the server quits;
// these are executed in response to SIGINT and are blocking
Shutdown []func() error
......
......@@ -371,6 +371,23 @@ func setupClientAuth(tlsConfigs []TLSConfig, config *tls.Config) error {
return nil
}
// RunFirstStartupFuncs runs all of the server's FirstStartup
// callback functions unless one of them returns an error first.
// It is up the caller's responsibility to call this only once and
// at the correct time. The functions here should not be executed
// at restarts or where the user does not explicitly start a new
// instance of the server.
func (s *Server) RunFirstStartupFuncs() error {
for _, vh := range s.vhosts {
for _, f := range vh.config.FirstStartup {
if err := f(); err != nil {
return err
}
}
}
return nil
}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment