From fb6ee865a949905f678aa7c7066c809664a8a4aa Mon Sep 17 00:00:00 2001 From: "Daniel, Dao Quang Minh" Date: Tue, 30 Sep 2014 04:30:58 -0400 Subject: [PATCH] save start error into State.Error when a container failed to start, saves the error message into State.Error so that it can be retrieved when calling `docker inspect` instead of having to look at the log Docker-DCO-1.1-Signed-off-by: Daniel, Dao Quang Minh (github: dqminh) --- daemon/container.go | 2 ++ daemon/state.go | 9 ++++++ integration-cli/docker_cli_start_test.go | 41 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/daemon/container.go b/daemon/container.go index e5c9fadace..f9500dff76 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -297,6 +297,8 @@ func (container *Container) Start() (err error) { // setup has been cleaned up properly defer func() { if err != nil { + container.setError(err) + container.toDisk() container.cleanup() } }() diff --git a/daemon/state.go b/daemon/state.go index b7dc149959..2dd57bd94b 100644 --- a/daemon/state.go +++ b/daemon/state.go @@ -15,6 +15,7 @@ type State struct { Restarting bool Pid int ExitCode int + Error string // contains last known error when starting the container StartedAt time.Time FinishedAt time.Time waitChan chan struct{} @@ -137,6 +138,7 @@ func (s *State) SetRunning(pid int) { } func (s *State) setRunning(pid int) { + s.Error = "" s.Running = true s.Paused = false s.Restarting = false @@ -179,6 +181,13 @@ func (s *State) SetRestarting(exitCode int) { s.Unlock() } +// setError sets the container's error state. This is useful when we want to +// know the error that occurred when container transits to another state +// when inspecting it +func (s *State) setError(err error) { + s.Error = err.Error() +} + func (s *State) IsRestarting() bool { s.Lock() res := s.Restarting diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go index af0a785185..72c0bfc4ef 100644 --- a/integration-cli/docker_cli_start_test.go +++ b/integration-cli/docker_cli_start_test.go @@ -68,3 +68,44 @@ func TestStartAttachCorrectExitCode(t *testing.T) { logDone("start - correct exit code returned with -a") } + +func TestStartRecordError(t *testing.T) { + defer deleteAllContainers() + + // when container runs successfully, we should not have state.Error + cmd(t, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top") + stateErr, err := inspectField("test", "State.Error") + if err != nil { + t.Fatalf("Failed to inspect %q state's error, got error %q", "test", err) + } + if stateErr != "" { + t.Fatalf("Expected to not have state error but got state.Error(%q)", stateErr) + } + + // Expect this to fail and records error because of ports conflict + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top")) + if err == nil { + t.Fatalf("Expected error but got none, output %q", out) + } + stateErr, err = inspectField("test2", "State.Error") + if err != nil { + t.Fatalf("Failed to inspect %q state's error, got error %q", "test2", err) + } + expected := "port is already allocated" + if stateErr == "" || !strings.Contains(stateErr, expected) { + t.Fatalf("State.Error(%q) does not include %q", stateErr, expected) + } + + // Expect the conflict to be resolved when we stop the initial container + cmd(t, "stop", "test") + cmd(t, "start", "test2") + stateErr, err = inspectField("test2", "State.Error") + if err != nil { + t.Fatalf("Failed to inspect %q state's error, got error %q", "test", err) + } + if stateErr != "" { + t.Fatalf("Expected to not have state error but got state.Error(%q)", stateErr) + } + + logDone("start - set state error when start fails") +}