Browse code

Merge remote-tracking branch 'origin/257-container_real_running_state-fix'

Solomon Hykes authored on 2013/04/05 07:34:18
Showing 3 changed files
... ...
@@ -7,9 +7,10 @@ import (
7 7
 	"io"
8 8
 	"io/ioutil"
9 9
 	"os"
10
+	"os/exec"
10 11
 	"path"
11 12
 	"sort"
12
-	"sync"
13
+	"strings"
13 14
 	"time"
14 15
 )
15 16
 
... ...
@@ -115,6 +116,7 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
115 115
 	if err := container.FromDisk(); err != nil {
116 116
 		return nil, err
117 117
 	}
118
+	container.State.initLock()
118 119
 	if container.Id != id {
119 120
 		return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
120 121
 	}
... ...
@@ -132,11 +134,26 @@ func (runtime *Runtime) Register(container *Container) error {
132 132
 	if err := validateId(container.Id); err != nil {
133 133
 		return err
134 134
 	}
135
+
136
+	// FIXME: if the container is supposed to be running but is not, auto restart it?
137
+	// If the container is supposed to be running, make sure of it
138
+	if container.State.Running {
139
+		if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
140
+			return err
141
+		} else {
142
+			if !strings.Contains(string(output), "RUNNING") {
143
+				Debugf("Container %s was supposed to be running be is not.", container.Id)
144
+				container.State.setStopped(-127)
145
+				if err := container.ToDisk(); err != nil {
146
+					return err
147
+				}
148
+			}
149
+		}
150
+	}
151
+
135 152
 	container.runtime = runtime
136 153
 	// Setup state lock (formerly in newState()
137
-	lock := new(sync.Mutex)
138
-	container.State.stateChangeLock = lock
139
-	container.State.stateChangeCond = sync.NewCond(lock)
154
+	container.State.initLock()
140 155
 	// Attach to stdout and stderr
141 156
 	container.stderr = newWriteBroadcaster()
142 157
 	container.stdout = newWriteBroadcaster()
... ...
@@ -259,7 +276,6 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
259 259
 		// If the auth file does not exist, keep going
260 260
 		return nil, err
261 261
 	}
262
-
263 262
 	runtime := &Runtime{
264 263
 		root:           root,
265 264
 		repository:     runtimeRepo,
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"os/user"
9 9
 	"sync"
10 10
 	"testing"
11
+	"time"
11 12
 )
12 13
 
13 14
 // FIXME: this is no longer needed
... ...
@@ -289,13 +290,48 @@ func TestRestore(t *testing.T) {
289 289
 		t.Fatal(err)
290 290
 	}
291 291
 	defer runtime1.Destroy(container1)
292
-	if len(runtime1.List()) != 1 {
293
-		t.Errorf("Expected 1 container, %v found", len(runtime1.List()))
292
+
293
+	// Create a second container meant to be killed
294
+	container2, err := runtime1.Create(&Config{
295
+		Image:     GetTestImage(runtime1).Id,
296
+		Cmd:       []string{"/bin/cat"},
297
+		OpenStdin: true,
298
+	},
299
+	)
300
+	if err != nil {
301
+		t.Fatal(err)
302
+	}
303
+	defer runtime1.Destroy(container2)
304
+
305
+	// Start the container non blocking
306
+	if err := container2.Start(); err != nil {
307
+		t.Fatal(err)
308
+	}
309
+
310
+	if !container2.State.Running {
311
+		t.Fatalf("Container %v should appear as running but isn't", container2.Id)
312
+	}
313
+
314
+	// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running'
315
+	cStdin, _ := container2.StdinPipe()
316
+	cStdin.Close()
317
+	if err := container2.WaitTimeout(time.Second); err != nil {
318
+		t.Fatal(err)
319
+	}
320
+	container2.State.Running = true
321
+	container2.ToDisk()
322
+
323
+	if len(runtime1.List()) != 2 {
324
+		t.Errorf("Expected 2 container, %v found", len(runtime1.List()))
294 325
 	}
295 326
 	if err := container1.Run(); err != nil {
296 327
 		t.Fatal(err)
297 328
 	}
298 329
 
330
+	if !container2.State.Running {
331
+		t.Fatalf("Container %v should appear as running but isn't", container2.Id)
332
+	}
333
+
299 334
 	// Here are are simulating a docker restart - that is, reloading all containers
300 335
 	// from scratch
301 336
 	runtime2, err := NewRuntimeFromDirectory(root)
... ...
@@ -303,14 +339,24 @@ func TestRestore(t *testing.T) {
303 303
 		t.Fatal(err)
304 304
 	}
305 305
 	defer nuke(runtime2)
306
-	if len(runtime2.List()) != 1 {
307
-		t.Errorf("Expected 1 container, %v found", len(runtime2.List()))
306
+	if len(runtime2.List()) != 2 {
307
+		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
308
+	}
309
+	runningCount := 0
310
+	for _, c := range runtime2.List() {
311
+		if c.State.Running {
312
+			t.Errorf("Running container found: %v (%v)", c.Id, c.Path)
313
+			runningCount++
314
+		}
315
+	}
316
+	if runningCount != 0 {
317
+		t.Fatalf("Expected 0 container alive, %d found", runningCount)
308 318
 	}
309
-	container2 := runtime2.Get(container1.Id)
310
-	if container2 == nil {
319
+	container3 := runtime2.Get(container1.Id)
320
+	if container3 == nil {
311 321
 		t.Fatal("Unable to Get container")
312 322
 	}
313
-	if err := container2.Run(); err != nil {
323
+	if err := container3.Run(); err != nil {
314 324
 		t.Fatal(err)
315 325
 	}
316 326
 }
... ...
@@ -39,6 +39,13 @@ func (s *State) setStopped(exitCode int) {
39 39
 	s.broadcast()
40 40
 }
41 41
 
42
+func (s *State) initLock() {
43
+	if s.stateChangeLock == nil {
44
+		s.stateChangeLock = &sync.Mutex{}
45
+		s.stateChangeCond = sync.NewCond(s.stateChangeLock)
46
+	}
47
+}
48
+
42 49
 func (s *State) broadcast() {
43 50
 	s.stateChangeLock.Lock()
44 51
 	s.stateChangeCond.Broadcast()