mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fixed lxc-wait race condition. Added unit test to try running multiple
containers in parallel.
This commit is contained in:
parent
f0c08b57ab
commit
f958bdba8a
2 changed files with 75 additions and 15 deletions
32
container.go
32
container.go
|
@ -121,21 +121,20 @@ func (container *Container) Start() error {
|
||||||
container.State.setRunning(container.cmd.Process.Pid)
|
container.State.setRunning(container.cmd.Process.Pid)
|
||||||
container.save()
|
container.save()
|
||||||
go container.monitor()
|
go container.monitor()
|
||||||
|
if err := exec.Command("/usr/bin/lxc-wait", "-n", container.Id, "-s", "RUNNING|STOPPED").Run(); err != nil {
|
||||||
// Wait until we are out of the STARTING state before returning
|
// lxc-wait might return an error if by the time we call it,
|
||||||
|
// the container we just started is already STOPPED.
|
||||||
|
// This is a rare race condition that happens for short living programs.
|
||||||
//
|
//
|
||||||
// Even though lxc-wait blocks until the container reaches a given state,
|
// A workaround is to discard lxc-wait errors if the container is not
|
||||||
// sometimes it returns an error code, which is why we have to retry.
|
// running anymore.
|
||||||
//
|
if !container.State.Running {
|
||||||
// This is a rare race condition that happens for short lived programs
|
|
||||||
for retries := 0; retries < 3; retries++ {
|
|
||||||
err := exec.Command("/usr/bin/lxc-wait", "-n", container.Id, "-s", "RUNNING|STOPPED").Run()
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return errors.New("Container failed to start")
|
return errors.New("Container failed to start")
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) Run() error {
|
func (container *Container) Run() error {
|
||||||
if err := container.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
|
@ -174,6 +173,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
||||||
func (container *Container) monitor() {
|
func (container *Container) monitor() {
|
||||||
// Wait for the program to exit
|
// Wait for the program to exit
|
||||||
container.cmd.Wait()
|
container.cmd.Wait()
|
||||||
|
exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
container.stdout.Close()
|
container.stdout.Close()
|
||||||
|
@ -183,7 +183,6 @@ func (container *Container) monitor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report status back
|
// Report status back
|
||||||
exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
|
||||||
container.State.setStopped(exitCode)
|
container.State.setStopped(exitCode)
|
||||||
container.save()
|
container.save()
|
||||||
}
|
}
|
||||||
|
@ -191,13 +190,18 @@ func (container *Container) monitor() {
|
||||||
func (container *Container) kill() error {
|
func (container *Container) kill() error {
|
||||||
// This will cause the main container process to receive a SIGKILL
|
// This will cause the main container process to receive a SIGKILL
|
||||||
if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Id).Run(); err != nil {
|
if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Id).Run(); err != nil {
|
||||||
|
log.Printf("Failed to lxc-stop %v", container.Id)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the container to be actually stopped
|
// Wait for the container to be actually stopped
|
||||||
if err := exec.Command("/usr/bin/lxc-wait", "-n", container.Id, "-s", "STOPPED").Run(); err != nil {
|
container.Wait()
|
||||||
return err
|
|
||||||
}
|
// Make sure the underlying LXC thinks it's stopped too
|
||||||
|
// LXC Issue: lxc-wait MIGHT say that the container doesn't exist
|
||||||
|
// That's probably because it was destroyed and it cannot find it anymore
|
||||||
|
// We are going to ignore lxc-wait's error
|
||||||
|
exec.Command("/usr/bin/lxc-wait", "-n", container.Id, "-s", "STOPPED").Run()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,3 +184,59 @@ func TestExitCode(t *testing.T) {
|
||||||
t.Errorf("Unexpected exit code %v", falseContainer.State.ExitCode)
|
t.Errorf("Unexpected exit code %v", falseContainer.State.ExitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultipleContainers(t *testing.T) {
|
||||||
|
docker, err := newTestDocker()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container1, err := docker.Create(
|
||||||
|
"container1",
|
||||||
|
"cat",
|
||||||
|
[]string{"/dev/zero"},
|
||||||
|
[]string{"/var/lib/docker/images/ubuntu"},
|
||||||
|
&Config{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer docker.Destroy(container1)
|
||||||
|
|
||||||
|
container2, err := docker.Create(
|
||||||
|
"container2",
|
||||||
|
"cat",
|
||||||
|
[]string{"/dev/zero"},
|
||||||
|
[]string{"/var/lib/docker/images/ubuntu"},
|
||||||
|
&Config{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer docker.Destroy(container2)
|
||||||
|
|
||||||
|
// Start both containers
|
||||||
|
if err := container1.Start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := container2.Start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are here, both containers should be running
|
||||||
|
if !container1.State.Running {
|
||||||
|
t.Fatal("Container not running")
|
||||||
|
}
|
||||||
|
if !container2.State.Running {
|
||||||
|
t.Fatal("Container not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill them
|
||||||
|
if err := container1.Kill(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := container2.Kill(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue