Browse code

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 <dqminh89@gmail.com> (github: dqminh)

Daniel, Dao Quang Minh authored on 2014/09/30 17:30:58
Showing 3 changed files
... ...
@@ -297,6 +297,8 @@ func (container *Container) Start() (err error) {
297 297
 	// setup has been cleaned up properly
298 298
 	defer func() {
299 299
 		if err != nil {
300
+			container.setError(err)
301
+			container.toDisk()
300 302
 			container.cleanup()
301 303
 		}
302 304
 	}()
... ...
@@ -15,6 +15,7 @@ type State struct {
15 15
 	Restarting bool
16 16
 	Pid        int
17 17
 	ExitCode   int
18
+	Error      string // contains last known error when starting the container
18 19
 	StartedAt  time.Time
19 20
 	FinishedAt time.Time
20 21
 	waitChan   chan struct{}
... ...
@@ -137,6 +138,7 @@ func (s *State) SetRunning(pid int) {
137 137
 }
138 138
 
139 139
 func (s *State) setRunning(pid int) {
140
+	s.Error = ""
140 141
 	s.Running = true
141 142
 	s.Paused = false
142 143
 	s.Restarting = false
... ...
@@ -179,6 +181,13 @@ func (s *State) SetRestarting(exitCode int) {
179 179
 	s.Unlock()
180 180
 }
181 181
 
182
+// setError sets the container's error state. This is useful when we want to
183
+// know the error that occurred when container transits to another state
184
+// when inspecting it
185
+func (s *State) setError(err error) {
186
+	s.Error = err.Error()
187
+}
188
+
182 189
 func (s *State) IsRestarting() bool {
183 190
 	s.Lock()
184 191
 	res := s.Restarting
... ...
@@ -68,3 +68,44 @@ func TestStartAttachCorrectExitCode(t *testing.T) {
68 68
 
69 69
 	logDone("start - correct exit code returned with -a")
70 70
 }
71
+
72
+func TestStartRecordError(t *testing.T) {
73
+	defer deleteAllContainers()
74
+
75
+	// when container runs successfully, we should not have state.Error
76
+	cmd(t, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top")
77
+	stateErr, err := inspectField("test", "State.Error")
78
+	if err != nil {
79
+		t.Fatalf("Failed to inspect %q state's error, got error %q", "test", err)
80
+	}
81
+	if stateErr != "" {
82
+		t.Fatalf("Expected to not have state error but got state.Error(%q)", stateErr)
83
+	}
84
+
85
+	// Expect this to fail and records error because of ports conflict
86
+	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top"))
87
+	if err == nil {
88
+		t.Fatalf("Expected error but got none, output %q", out)
89
+	}
90
+	stateErr, err = inspectField("test2", "State.Error")
91
+	if err != nil {
92
+		t.Fatalf("Failed to inspect %q state's error, got error %q", "test2", err)
93
+	}
94
+	expected := "port is already allocated"
95
+	if stateErr == "" || !strings.Contains(stateErr, expected) {
96
+		t.Fatalf("State.Error(%q) does not include %q", stateErr, expected)
97
+	}
98
+
99
+	// Expect the conflict to be resolved when we stop the initial container
100
+	cmd(t, "stop", "test")
101
+	cmd(t, "start", "test2")
102
+	stateErr, err = inspectField("test2", "State.Error")
103
+	if err != nil {
104
+		t.Fatalf("Failed to inspect %q state's error, got error %q", "test", err)
105
+	}
106
+	if stateErr != "" {
107
+		t.Fatalf("Expected to not have state error but got state.Error(%q)", stateErr)
108
+	}
109
+
110
+	logDone("start - set state error when start fails")
111
+}