mirror of https://github.com/docker/docs.git
use engine-api for events
Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
parent
ae7174fe4c
commit
622b509274
|
|
@ -36,10 +36,10 @@ func TestHandle(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
event.Event.Status = "status"
|
event.Message.Status = "status"
|
||||||
event.Event.ID = "id"
|
event.Message.ID = "id"
|
||||||
event.Event.From = "from"
|
event.Message.From = "from"
|
||||||
event.Event.Time = 0
|
event.Message.Time = 0
|
||||||
event.Actor.Attributes = make(map[string]string)
|
event.Actor.Attributes = make(map[string]string)
|
||||||
event.Actor.Attributes["nodevent.name"] = event.Engine.Name
|
event.Actor.Attributes["nodevent.name"] = event.Engine.Name
|
||||||
event.Actor.Attributes["nodevent.id"] = event.Engine.ID
|
event.Actor.Attributes["nodevent.id"] = event.Engine.ID
|
||||||
|
|
@ -48,7 +48,7 @@ func TestHandle(t *testing.T) {
|
||||||
|
|
||||||
assert.NoError(t, eh.Handle(event))
|
assert.NoError(t, eh.Handle(event))
|
||||||
|
|
||||||
event.Event.From = "from node:node_name"
|
event.Message.From = "from node:node_name"
|
||||||
|
|
||||||
data, err := json.Marshal(event)
|
data, err := json.Marshal(event)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/version"
|
"github.com/docker/docker/pkg/version"
|
||||||
engineapi "github.com/docker/engine-api/client"
|
engineapi "github.com/docker/engine-api/client"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/events"
|
||||||
"github.com/docker/engine-api/types/filters"
|
"github.com/docker/engine-api/types/filters"
|
||||||
networktypes "github.com/docker/engine-api/types/network"
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
engineapinop "github.com/docker/swarm/api/nopclient"
|
engineapinop "github.com/docker/swarm/api/nopclient"
|
||||||
|
|
@ -127,6 +128,7 @@ type Engine struct {
|
||||||
failureCount int
|
failureCount int
|
||||||
overcommitRatio int64
|
overcommitRatio int64
|
||||||
opts *EngineOpts
|
opts *EngineOpts
|
||||||
|
eventsMonitor *EventsMonitor
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEngine is exported
|
// NewEngine is exported
|
||||||
|
|
@ -188,16 +190,14 @@ func (e *Engine) Connect(config *tls.Config) error {
|
||||||
func (e *Engine) StartMonitorEvents() {
|
func (e *Engine) StartMonitorEvents() {
|
||||||
log.WithFields(log.Fields{"name": e.Name, "id": e.ID}).Debug("Start monitoring events")
|
log.WithFields(log.Fields{"name": e.Name, "id": e.ID}).Debug("Start monitoring events")
|
||||||
ec := make(chan error)
|
ec := make(chan error)
|
||||||
e.client.StartMonitorEvents(e.handler, ec)
|
e.eventsMonitor.Start(ec)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := <-ec; err != nil {
|
if err := <-ec; err != nil {
|
||||||
log.WithFields(log.Fields{"name": e.Name, "id": e.ID}).Errorf("Error monitoring events: %s.", err)
|
|
||||||
if !strings.Contains(err.Error(), "EOF") {
|
if !strings.Contains(err.Error(), "EOF") {
|
||||||
// failing node reconnect should use back-off strategy
|
// failing node reconnect should use back-off strategy
|
||||||
<-e.refreshDelayer.Wait(e.getFailureCount())
|
<-e.refreshDelayer.Wait(e.getFailureCount())
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"name": e.Name, "id": e.ID}).Errorf("Restart event monitoring.")
|
|
||||||
e.StartMonitorEvents()
|
e.StartMonitorEvents()
|
||||||
}
|
}
|
||||||
close(ec)
|
close(ec)
|
||||||
|
|
@ -208,6 +208,7 @@ func (e *Engine) StartMonitorEvents() {
|
||||||
func (e *Engine) ConnectWithClient(client dockerclient.Client, apiClient engineapi.APIClient) error {
|
func (e *Engine) ConnectWithClient(client dockerclient.Client, apiClient engineapi.APIClient) error {
|
||||||
e.client = client
|
e.client = client
|
||||||
e.apiClient = apiClient
|
e.apiClient = apiClient
|
||||||
|
e.eventsMonitor = NewEventsMonitor(e.apiClient, e.handler)
|
||||||
|
|
||||||
// Fetch the engine labels.
|
// Fetch the engine labels.
|
||||||
if err := e.updateSpecs(); err != nil {
|
if err := e.updateSpecs(); err != nil {
|
||||||
|
|
@ -246,7 +247,8 @@ func (e *Engine) Disconnect() {
|
||||||
|
|
||||||
// close the chan
|
// close the chan
|
||||||
close(e.stopCh)
|
close(e.stopCh)
|
||||||
e.client.StopAllMonitorEvents()
|
e.eventsMonitor.Stop()
|
||||||
|
|
||||||
// close idle connections
|
// close idle connections
|
||||||
if dc, ok := e.client.(*dockerclient.DockerClient); ok {
|
if dc, ok := e.client.(*dockerclient.DockerClient); ok {
|
||||||
closeIdleConnections(dc.HTTPClient)
|
closeIdleConnections(dc.HTTPClient)
|
||||||
|
|
@ -788,7 +790,7 @@ func (e *Engine) refreshLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !healthy {
|
if !healthy {
|
||||||
e.client.StopAllMonitorEvents()
|
e.eventsMonitor.Stop()
|
||||||
e.StartMonitorEvents()
|
e.StartMonitorEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -811,12 +813,12 @@ func (e *Engine) emitEvent(event string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ev := &Event{
|
ev := &Event{
|
||||||
Event: dockerclient.Event{
|
Message: events.Message{
|
||||||
Status: event,
|
Status: event,
|
||||||
From: "swarm",
|
From: "swarm",
|
||||||
Type: "swarm",
|
Type: "swarm",
|
||||||
Action: event,
|
Action: event,
|
||||||
Actor: dockerclient.Actor{
|
Actor: events.Actor{
|
||||||
Attributes: make(map[string]string),
|
Attributes: make(map[string]string),
|
||||||
},
|
},
|
||||||
Time: time.Now().Unix(),
|
Time: time.Now().Unix(),
|
||||||
|
|
@ -1111,10 +1113,10 @@ func (e *Engine) String() string {
|
||||||
return fmt.Sprintf("engine %s addr %s", e.ID, e.Addr)
|
return fmt.Sprintf("engine %s addr %s", e.ID, e.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) handler(ev *dockerclient.Event, _ chan error, args ...interface{}) {
|
func (e *Engine) handler(msg events.Message) error {
|
||||||
// Something changed - refresh our internal state.
|
// Something changed - refresh our internal state.
|
||||||
|
|
||||||
switch ev.Type {
|
switch msg.Type {
|
||||||
case "network":
|
case "network":
|
||||||
e.RefreshNetworks()
|
e.RefreshNetworks()
|
||||||
case "volume":
|
case "volume":
|
||||||
|
|
@ -1122,15 +1124,15 @@ func (e *Engine) handler(ev *dockerclient.Event, _ chan error, args ...interface
|
||||||
case "image":
|
case "image":
|
||||||
e.RefreshImages()
|
e.RefreshImages()
|
||||||
case "container":
|
case "container":
|
||||||
switch ev.Action {
|
switch msg.Action {
|
||||||
case "die", "kill", "oom", "pause", "start", "restart", "stop", "unpause", "rename":
|
case "die", "kill", "oom", "pause", "start", "restart", "stop", "unpause", "rename":
|
||||||
e.refreshContainer(ev.ID, true)
|
e.refreshContainer(msg.ID, true)
|
||||||
default:
|
default:
|
||||||
e.refreshContainer(ev.ID, false)
|
e.refreshContainer(msg.ID, false)
|
||||||
}
|
}
|
||||||
case "":
|
case "":
|
||||||
// docker < 1.10
|
// docker < 1.10
|
||||||
switch ev.Status {
|
switch msg.Status {
|
||||||
case "pull", "untag", "delete", "commit":
|
case "pull", "untag", "delete", "commit":
|
||||||
// These events refer to images so there's no need to update
|
// These events refer to images so there's no need to update
|
||||||
// containers.
|
// containers.
|
||||||
|
|
@ -1138,12 +1140,12 @@ func (e *Engine) handler(ev *dockerclient.Event, _ chan error, args ...interface
|
||||||
case "die", "kill", "oom", "pause", "start", "stop", "unpause", "rename":
|
case "die", "kill", "oom", "pause", "start", "stop", "unpause", "rename":
|
||||||
// If the container state changes, we have to do an inspect in
|
// If the container state changes, we have to do an inspect in
|
||||||
// order to update container.Info and get the new NetworkSettings.
|
// order to update container.Info and get the new NetworkSettings.
|
||||||
e.refreshContainer(ev.ID, true)
|
e.refreshContainer(msg.ID, true)
|
||||||
e.RefreshVolumes()
|
e.RefreshVolumes()
|
||||||
e.RefreshNetworks()
|
e.RefreshNetworks()
|
||||||
default:
|
default:
|
||||||
// Otherwise, do a "soft" refresh of the container.
|
// Otherwise, do a "soft" refresh of the container.
|
||||||
e.refreshContainer(ev.ID, false)
|
e.refreshContainer(msg.ID, false)
|
||||||
e.RefreshVolumes()
|
e.RefreshVolumes()
|
||||||
e.RefreshNetworks()
|
e.RefreshNetworks()
|
||||||
}
|
}
|
||||||
|
|
@ -1152,15 +1154,15 @@ func (e *Engine) handler(ev *dockerclient.Event, _ chan error, args ...interface
|
||||||
|
|
||||||
// If there is no event handler registered, abort right now.
|
// If there is no event handler registered, abort right now.
|
||||||
if e.eventHandler == nil {
|
if e.eventHandler == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
event := &Event{
|
event := &Event{
|
||||||
Engine: e,
|
Engine: e,
|
||||||
Event: *ev,
|
Message: msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
e.eventHandler.Handle(event)
|
return e.eventHandler.Handle(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddContainer injects a container into the internal state.
|
// AddContainer injects a container into the internal state.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
engineapi "github.com/docker/engine-api/client"
|
engineapi "github.com/docker/engine-api/client"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
containertypes "github.com/docker/engine-api/types/container"
|
containertypes "github.com/docker/engine-api/types/container"
|
||||||
|
"github.com/docker/engine-api/types/events"
|
||||||
"github.com/docker/engine-api/types/filters"
|
"github.com/docker/engine-api/types/filters"
|
||||||
networktypes "github.com/docker/engine-api/types/network"
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
engineapimock "github.com/docker/swarm/api/mockclient"
|
engineapimock "github.com/docker/swarm/api/mockclient"
|
||||||
|
|
@ -25,6 +26,13 @@ import (
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type infinitRead struct{}
|
||||||
|
|
||||||
|
func (infinitRead) Read(p []byte) (n int, err error) {
|
||||||
|
p = append(p, 1)
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mockInfo = types.Info{
|
mockInfo = types.Info{
|
||||||
ID: "id",
|
ID: "id",
|
||||||
|
|
@ -180,7 +188,7 @@ func TestEngineCpusMemory(t *testing.T) {
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil)
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
|
|
||||||
assert.NoError(t, engine.ConnectWithClient(client, apiClient))
|
assert.NoError(t, engine.ConnectWithClient(client, apiClient))
|
||||||
assert.True(t, engine.isConnected())
|
assert.True(t, engine.isConnected())
|
||||||
|
|
@ -210,7 +218,7 @@ func TestEngineSpecs(t *testing.T) {
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil)
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
|
|
||||||
assert.NoError(t, engine.ConnectWithClient(client, apiClient))
|
assert.NoError(t, engine.ConnectWithClient(client, apiClient))
|
||||||
assert.True(t, engine.isConnected())
|
assert.True(t, engine.isConnected())
|
||||||
|
|
@ -243,7 +251,7 @@ func TestEngineState(t *testing.T) {
|
||||||
apiClient.On("VolumeList", mock.Anything,
|
apiClient.On("VolumeList", mock.Anything,
|
||||||
mock.AnythingOfType("Args"),
|
mock.AnythingOfType("Args"),
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
|
|
||||||
// The client will return one container at first, then a second one will appear.
|
// The client will return one container at first, then a second one will appear.
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
|
||||||
|
|
@ -265,7 +273,7 @@ func TestEngineState(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fake an event which will trigger a refresh. The second container will appear.
|
// Fake an event which will trigger a refresh. The second container will appear.
|
||||||
engine.handler(&dockerclient.Event{ID: "two", Status: "created"}, nil)
|
engine.handler(events.Message{ID: "two", Status: "created"})
|
||||||
containers = engine.Containers()
|
containers = engine.Containers()
|
||||||
assert.Len(t, containers, 2)
|
assert.Len(t, containers, 2)
|
||||||
if containers[0].ID != "one" && containers[1].ID != "one" {
|
if containers[0].ID != "one" && containers[1].ID != "one" {
|
||||||
|
|
@ -305,7 +313,8 @@ func TestCreateContainer(t *testing.T) {
|
||||||
apiClient.On("VolumeList", mock.Anything,
|
apiClient.On("VolumeList", mock.Anything,
|
||||||
mock.AnythingOfType("Args"),
|
mock.AnythingOfType("Args"),
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
|
client.On("ListContainers", true, false, "").Return([]dockerclient.Container{}, nil).Once()
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
|
||||||
// filterArgs1 := filters.NewArgs()
|
// filterArgs1 := filters.NewArgs()
|
||||||
// filterArgs1.Add("id", id)
|
// filterArgs1.Add("id", id)
|
||||||
|
|
@ -419,7 +428,7 @@ func TestUsedCpus(t *testing.T) {
|
||||||
apiClient.On("VolumeList", mock.Anything,
|
apiClient.On("VolumeList", mock.Anything,
|
||||||
mock.AnythingOfType("Args"),
|
mock.AnythingOfType("Args"),
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{{ID: "test"}}, nil).Once()
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{{ID: "test"}}, nil).Once()
|
||||||
apiClient.On("ContainerInspect", mock.Anything, "test").Return(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{HostConfig: &containertypes.HostConfig{Resources: containertypes.Resources{CPUShares: cpuShares}}}, Config: &containertypes.Config{}, NetworkSettings: &types.NetworkSettings{Networks: nil}}, nil).Once()
|
apiClient.On("ContainerInspect", mock.Anything, "test").Return(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{HostConfig: &containertypes.HostConfig{Resources: containertypes.Resources{CPUShares: cpuShares}}}, Config: &containertypes.Config{}, NetworkSettings: &types.NetworkSettings{Networks: nil}}, nil).Once()
|
||||||
|
|
@ -458,7 +467,7 @@ func TestContainerRemovedDuringRefresh(t *testing.T) {
|
||||||
mock.AnythingOfType("Args"),
|
mock.AnythingOfType("Args"),
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{container1, container2}, nil)
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{container1, container2}, nil)
|
||||||
apiClient.On("ContainerInspect", mock.Anything, "c1").Return(info1, errors.New("Not found"))
|
apiClient.On("ContainerInspect", mock.Anything, "c1").Return(info1, errors.New("Not found"))
|
||||||
apiClient.On("ContainerInspect", mock.Anything, "c2").Return(info2, nil)
|
apiClient.On("ContainerInspect", mock.Anything, "c2").Return(info2, nil)
|
||||||
|
|
@ -489,8 +498,7 @@ func TestDisconnect(t *testing.T) {
|
||||||
apiClient.On("VolumeList", mock.Anything,
|
apiClient.On("VolumeList", mock.Anything,
|
||||||
mock.AnythingOfType("Args"),
|
mock.AnythingOfType("Args"),
|
||||||
).Return(types.VolumesListResponse{}, nil)
|
).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{infinitRead{}}, nil)
|
||||||
client.On("StopAllMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
|
||||||
|
|
||||||
// The client will return one container at first, then a second one will appear.
|
// The client will return one container at first, then a second one will appear.
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/samalba/dockerclient"
|
"github.com/docker/engine-api/types/events"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event is exported
|
// Event is exported
|
||||||
type Event struct {
|
type Event struct {
|
||||||
dockerclient.Event
|
events.Message
|
||||||
Engine *Engine `json:"-"`
|
Engine *Engine `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/engine-api/client"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/events"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
//EventsMonitor monitors events
|
||||||
|
type EventsMonitor struct {
|
||||||
|
stopChan chan struct{}
|
||||||
|
cli client.APIClient
|
||||||
|
handler func(msg events.Message) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type decodingResult struct {
|
||||||
|
msg events.Message
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEventsMonitor returns an EventsMonitor
|
||||||
|
func NewEventsMonitor(cli client.APIClient, handler func(msg events.Message) error) *EventsMonitor {
|
||||||
|
return &EventsMonitor{
|
||||||
|
cli: cli,
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the EventsMonitor
|
||||||
|
func (em *EventsMonitor) Start(ec chan error) {
|
||||||
|
em.stopChan = make(chan struct{})
|
||||||
|
|
||||||
|
responseBody, err := em.cli.Events(context.TODO(), types.EventsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
ec <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resultChan := make(chan decodingResult)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
dec := json.NewDecoder(responseBody)
|
||||||
|
for {
|
||||||
|
var result decodingResult
|
||||||
|
result.err = dec.Decode(&result.msg)
|
||||||
|
resultChan <- result
|
||||||
|
if result.err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer responseBody.Close()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-em.stopChan:
|
||||||
|
ec <- nil
|
||||||
|
return
|
||||||
|
case result := <-resultChan:
|
||||||
|
if result.err != nil {
|
||||||
|
ec <- result.err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := em.handler(result.msg); err != nil {
|
||||||
|
ec <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the EventsMonitor
|
||||||
|
func (em *EventsMonitor) Stop() {
|
||||||
|
if em.stopChan == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
close(em.stopChan)
|
||||||
|
}
|
||||||
|
|
@ -143,7 +143,7 @@ func TestImportImage(t *testing.T) {
|
||||||
mock.AnythingOfType("NetworkListOptions"),
|
mock.AnythingOfType("NetworkListOptions"),
|
||||||
).Return([]types.NetworkResource{}, nil)
|
).Return([]types.NetworkResource{}, nil)
|
||||||
apiClient.On("VolumeList", mock.Anything, mock.Anything).Return(types.VolumesListResponse{}, nil)
|
apiClient.On("VolumeList", mock.Anything, mock.Anything).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{bytes.NewBufferString("")}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil).Once()
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil).Once()
|
||||||
|
|
||||||
|
|
@ -196,7 +196,7 @@ func TestLoadImage(t *testing.T) {
|
||||||
mock.AnythingOfType("NetworkListOptions"),
|
mock.AnythingOfType("NetworkListOptions"),
|
||||||
).Return([]types.NetworkResource{}, nil)
|
).Return([]types.NetworkResource{}, nil)
|
||||||
apiClient.On("VolumeList", mock.Anything, mock.Anything).Return(types.VolumesListResponse{}, nil)
|
apiClient.On("VolumeList", mock.Anything, mock.Anything).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{bytes.NewBufferString("")}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil)
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil).Once()
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil).Once()
|
||||||
|
|
||||||
|
|
@ -252,7 +252,7 @@ func TestTagImage(t *testing.T) {
|
||||||
mock.AnythingOfType("NetworkListOptions"),
|
mock.AnythingOfType("NetworkListOptions"),
|
||||||
).Return([]types.NetworkResource{}, nil)
|
).Return([]types.NetworkResource{}, nil)
|
||||||
apiClient.On("VolumeList", mock.Anything, mock.Anything).Return(types.VolumesListResponse{}, nil)
|
apiClient.On("VolumeList", mock.Anything, mock.Anything).Return(types.VolumesListResponse{}, nil)
|
||||||
client.On("StartMonitorEvents", mock.Anything, mock.Anything, mock.Anything).Return()
|
apiClient.On("Events", mock.Anything, mock.AnythingOfType("EventsOptions")).Return(&nopCloser{bytes.NewBufferString("")}, nil)
|
||||||
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return(images, nil)
|
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return(images, nil)
|
||||||
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil).Once()
|
apiClient.On("ContainerList", mock.Anything, types.ContainerListOptions{All: true, Size: false}).Return([]types.Container{}, nil).Once()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue