diff --git a/api/server/server.go b/api/server/server.go index d244d2a0ce..5b84321ed2 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -1578,7 +1578,15 @@ func ServeApi(job *engine.Job) engine.Status { chErrors <- err return } - chErrors <- srv.Serve() + job.Eng.OnShutdown(func() { + if err := srv.Close(); err != nil { + log.Error(err) + } + }) + if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") { + err = nil + } + chErrors <- err }() } diff --git a/docker/daemon.go b/docker/daemon.go index e3bd06d901..b2a985b221 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -186,8 +186,9 @@ func mainDaemon() { errAPI := <-serveAPIWait // If we have an error here it is unique to API (as daemonErr would have // exited the daemon process above) - if errAPI != nil { - log.Errorf("Shutting down due to ServeAPI error: %v", errAPI) - } eng.Shutdown() + if errAPI != nil { + log.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) + } + } diff --git a/engine/engine.go b/engine/engine.go index e8286d89f7..84292ad2bd 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -46,18 +46,19 @@ func unregister(name string) { // It acts as a store for *containers*, and allows manipulation of these // containers by executing *jobs*. type Engine struct { - handlers map[string]Handler - catchall Handler - hack Hack // data for temporary hackery (see hack.go) - id string - Stdout io.Writer - Stderr io.Writer - Stdin io.Reader - Logging bool - tasks sync.WaitGroup - l sync.RWMutex // lock for shutdown - shutdown bool - onShutdown []func() // shutdown handlers + handlers map[string]Handler + catchall Handler + hack Hack // data for temporary hackery (see hack.go) + id string + Stdout io.Writer + Stderr io.Writer + Stdin io.Reader + Logging bool + tasks sync.WaitGroup + l sync.RWMutex // lock for shutdown + shutdownWait sync.WaitGroup + shutdown bool + onShutdown []func() // shutdown handlers } func (eng *Engine) Register(name string, handler Handler) error { @@ -143,6 +144,7 @@ func (eng *Engine) Job(name string, args ...string) *Job { func (eng *Engine) OnShutdown(h func()) { eng.l.Lock() eng.onShutdown = append(eng.onShutdown, h) + eng.shutdownWait.Add(1) eng.l.Unlock() } @@ -156,6 +158,7 @@ func (eng *Engine) Shutdown() { eng.l.Lock() if eng.shutdown { eng.l.Unlock() + eng.shutdownWait.Wait() return } eng.shutdown = true @@ -180,17 +183,15 @@ func (eng *Engine) Shutdown() { // Call shutdown handlers, if any. // Timeout after 10 seconds. - var wg sync.WaitGroup for _, h := range eng.onShutdown { - wg.Add(1) go func(h func()) { - defer wg.Done() h() + eng.shutdownWait.Done() }(h) } done := make(chan struct{}) go func() { - wg.Wait() + eng.shutdownWait.Wait() close(done) }() select { diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 49b43c2f28..c515a63787 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -800,3 +800,31 @@ func TestDaemonDots(t *testing.T) { logDone("daemon - test dots on INFO") } + +func TestDaemonUnixSockCleanedUp(t *testing.T) { + d := NewDaemon(t) + dir, err := ioutil.TempDir("", "socket-cleanup-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + sockPath := filepath.Join(dir, "docker.sock") + if err := d.Start("--host", "unix://"+sockPath); err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(sockPath); err != nil { + t.Fatal("socket does not exist") + } + + if err := d.Stop(); err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(sockPath); err == nil || !os.IsNotExist(err) { + t.Fatal("unix socket is not cleaned up") + } + + logDone("daemon - unix socket is cleaned up") +}