From 0e3f2f2ac07c69922bd837f864219a088d243248 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Fri, 6 Mar 2015 15:44:31 -0500 Subject: [PATCH] Ensure clean engine shutdown on startup errors Previously on error either from the daemon or from the api it is just exiting with exit status 1 but not performing a shutdown. This can produce insconsistent state depending on where the error came from. This makes sure that before we exit on error that the engine gets fully shutdown. Signed-off-by: Brian Goff --- daemon/daemon.go | 17 +++++++++-------- docker/daemon.go | 29 ++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index 31fcded4c6..7e38966cd3 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1002,14 +1002,6 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) trustStore: t, statsCollector: newStatsCollector(1 * time.Second), } - if err := daemon.restore(); err != nil { - return nil, err - } - - // set up filesystem watch on resolv.conf for network changes - if err := daemon.setupResolvconfWatcher(); err != nil { - return nil, err - } // Setup shutdown handlers // FIXME: can these shutdown handlers be registered closer to their source? @@ -1030,6 +1022,15 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) } }) + if err := daemon.restore(); err != nil { + return nil, err + } + + // set up filesystem watch on resolv.conf for network changes + if err := daemon.setupResolvconfWatcher(); err != nil { + return nil, err + } + return daemon, nil } diff --git a/docker/daemon.go b/docker/daemon.go index 5f799a46a5..38f808a124 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -101,11 +101,17 @@ func mainDaemon() { // load the daemon in the background so we can immediately start // the http api so that connections don't fail while the daemon // is booting + daemonWait := make(chan struct{}) go func() { + defer func() { + close(daemonWait) + }() d, err := daemon.NewDaemon(daemonCfg, eng) if err != nil { - log.Fatal(err) + log.Error(err) + return } + log.Infof("docker daemon: %s %s; execdriver: %s; graphdriver: %s", dockerversion.VERSION, dockerversion.GITCOMMIT, @@ -114,7 +120,8 @@ func mainDaemon() { ) if err := d.Install(eng); err != nil { - log.Fatal(err) + log.Error(err) + return } b := &builder.BuilderJob{eng, d} @@ -123,8 +130,11 @@ func mainDaemon() { // after the daemon is done setting up we can tell the api to start // accepting connections if err := eng.Job("acceptconnections").Run(); err != nil { - log.Fatal(err) + log.Error(err) + return } + + log.Debugf("daemon finished") }() // Serve api @@ -141,7 +151,16 @@ func mainDaemon() { job.Setenv("TlsCert", *flCert) job.Setenv("TlsKey", *flKey) job.SetenvBool("BufferRequests", true) - if err := job.Run(); err != nil { - log.Fatal(err) + err := job.Run() + + // Wait for the daemon startup goroutine to finish + // This makes sure we can actually cleanly shutdown the daemon + log.Infof("waiting for daemon to initialize") + <-daemonWait + eng.Shutdown() + if err != nil { + // log errors here so the log output looks more consistent + log.Fatalf("shutting down daemon due to errors: %v", err) } + }