2016-06-09 06:04:53 -04:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
2016-06-09 11:32:20 -04:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
|
2016-06-09 06:04:53 -04:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2016-06-09 11:32:20 -04:00
|
|
|
"github.com/Sirupsen/logrus"
|
2016-06-09 06:04:53 -04:00
|
|
|
"github.com/docker/docker/api/client"
|
2016-06-09 11:32:20 -04:00
|
|
|
"github.com/docker/docker/api/client/system"
|
2016-06-09 06:04:53 -04:00
|
|
|
clientapi "github.com/docker/engine-api/client"
|
2016-06-09 11:32:20 -04:00
|
|
|
"github.com/docker/engine-api/types"
|
|
|
|
"github.com/docker/engine-api/types/events"
|
|
|
|
"github.com/docker/engine-api/types/filters"
|
2016-06-09 06:04:53 -04:00
|
|
|
)
|
|
|
|
|
2016-08-09 05:33:36 -04:00
|
|
|
func waitExitOrRemoved(dockerCli *client.DockerCli, ctx context.Context, containerID string, waitRemove bool) (chan int, error) {
|
2016-06-09 11:32:20 -04:00
|
|
|
if len(containerID) == 0 {
|
|
|
|
// containerID can never be empty
|
|
|
|
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
|
|
|
|
}
|
|
|
|
|
|
|
|
var exitCode int
|
2016-08-09 05:33:36 -04:00
|
|
|
statusChan := make(chan int)
|
2016-06-09 11:32:20 -04:00
|
|
|
exitChan := make(chan struct{})
|
|
|
|
detachChan := make(chan struct{})
|
|
|
|
destroyChan := make(chan struct{})
|
2016-08-09 05:33:36 -04:00
|
|
|
eventsErr := make(chan error)
|
2016-06-09 11:32:20 -04:00
|
|
|
|
|
|
|
// Start watch events
|
|
|
|
eh := system.InitEventHandler()
|
|
|
|
eh.Handle("die", func(e events.Message) {
|
|
|
|
if len(e.Actor.Attributes) > 0 {
|
|
|
|
for k, v := range e.Actor.Attributes {
|
|
|
|
if k == "exitCode" {
|
|
|
|
var err error
|
|
|
|
if exitCode, err = strconv.Atoi(v); err != nil {
|
|
|
|
logrus.Errorf("Can't convert %q to int: %v", v, err)
|
|
|
|
}
|
|
|
|
close(exitChan)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
eh.Handle("detach", func(e events.Message) {
|
|
|
|
exitCode = 0
|
|
|
|
close(detachChan)
|
|
|
|
})
|
|
|
|
eh.Handle("destroy", func(e events.Message) {
|
|
|
|
close(destroyChan)
|
|
|
|
})
|
|
|
|
|
|
|
|
eventChan := make(chan events.Message)
|
|
|
|
go eh.Watch(eventChan)
|
|
|
|
|
|
|
|
// Get events via Events API
|
|
|
|
f := filters.NewArgs()
|
|
|
|
f.Add("type", "container")
|
|
|
|
f.Add("container", containerID)
|
|
|
|
options := types.EventsOptions{
|
|
|
|
Filters: f,
|
|
|
|
}
|
|
|
|
resBody, err := dockerCli.Client().Events(ctx, options)
|
|
|
|
if err != nil {
|
2016-08-09 05:33:36 -04:00
|
|
|
return nil, fmt.Errorf("can't get events from daemon: %v", err)
|
2016-06-09 11:32:20 -04:00
|
|
|
}
|
|
|
|
|
2016-08-09 05:33:36 -04:00
|
|
|
go func() {
|
|
|
|
eventsErr <- system.DecodeEvents(resBody, func(event events.Message, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("decode events error: %v", err)
|
|
|
|
}
|
|
|
|
eventChan <- event
|
2016-06-09 11:32:20 -04:00
|
|
|
return nil
|
2016-08-09 05:33:36 -04:00
|
|
|
})
|
|
|
|
close(eventChan)
|
|
|
|
}()
|
2016-06-09 11:32:20 -04:00
|
|
|
|
2016-08-09 05:33:36 -04:00
|
|
|
go func() {
|
|
|
|
var waitErr error
|
|
|
|
if waitRemove {
|
|
|
|
select {
|
|
|
|
case <-destroyChan:
|
|
|
|
// keep exitcode and return
|
|
|
|
case <-detachChan:
|
|
|
|
exitCode = 0
|
|
|
|
case waitErr = <-eventsErr:
|
|
|
|
exitCode = 125
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
select {
|
|
|
|
case <-exitChan:
|
|
|
|
// keep exitcode and return
|
|
|
|
case <-detachChan:
|
|
|
|
exitCode = 0
|
|
|
|
case waitErr = <-eventsErr:
|
|
|
|
exitCode = 125
|
|
|
|
}
|
2016-06-09 11:32:20 -04:00
|
|
|
}
|
2016-08-09 05:33:36 -04:00
|
|
|
if waitErr != nil {
|
|
|
|
logrus.Errorf("%v", waitErr)
|
2016-06-09 11:32:20 -04:00
|
|
|
}
|
2016-08-09 05:33:36 -04:00
|
|
|
statusChan <- exitCode
|
|
|
|
|
|
|
|
resBody.Close()
|
|
|
|
}()
|
|
|
|
return statusChan, nil
|
2016-06-09 11:32:20 -04:00
|
|
|
}
|
|
|
|
|
2016-07-21 06:03:37 -04:00
|
|
|
// getExitCode performs an inspect on the container. It returns
|
2016-06-09 06:04:53 -04:00
|
|
|
// the running state and the exit code.
|
|
|
|
func getExitCode(dockerCli *client.DockerCli, ctx context.Context, containerID string) (bool, int, error) {
|
|
|
|
c, err := dockerCli.Client().ContainerInspect(ctx, containerID)
|
|
|
|
if err != nil {
|
|
|
|
// If we can't connect, then the daemon probably died.
|
|
|
|
if err != clientapi.ErrConnectionFailed {
|
|
|
|
return false, -1, err
|
|
|
|
}
|
|
|
|
return false, -1, nil
|
|
|
|
}
|
|
|
|
return c.State.Running, c.State.ExitCode, nil
|
|
|
|
}
|