Commit fc6afe2a authored by Matt Holt's avatar Matt Holt

Merge pull request #333 from mholt/firststartup

startup: Only run commands at first startup
parents 51d2ff4e 5cced604
......@@ -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