| ... | ... |
@@ -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() |