Files
godoxy/src/watcher/docker_watcher.go

96 lines
2.0 KiB
Go

package watcher
import (
"context"
"fmt"
"time"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
D "github.com/yusing/go-proxy/docker"
E "github.com/yusing/go-proxy/error"
)
type DockerWatcher struct {
host string
}
func NewDockerWatcher(host string) *DockerWatcher {
return &DockerWatcher{host: host}
}
func (w *DockerWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.NestedError) {
eventCh := make(chan Event)
errCh := make(chan E.NestedError)
started := make(chan struct{})
go func() {
defer close(errCh)
var cl D.Client
var err E.NestedError
for range 3 {
cl, err = D.ConnectClient(w.host)
if err.IsNil() {
break
}
errCh <- E.From(err)
time.Sleep(1 * time.Second)
}
if err.IsNotNil() {
errCh <- E.Failure("connecting to docker")
return
}
cEventCh, cErrCh := cl.Events(ctx, dwOptions)
started <- struct{}{}
for {
select {
case <-ctx.Done():
if err := <-cErrCh; err != nil {
errCh <- E.From(err)
}
return
case msg := <-cEventCh:
var Action Action
switch msg.Action {
case events.ActionStart:
Action = ActionCreated
case events.ActionDie:
Action = ActionDeleted
default: // NOTE: should not happen
Action = ActionModified
}
eventCh <- Event{
ActorName: fmt.Sprintf("container %q", msg.Actor.Attributes["name"]),
Action: Action,
}
case err := <-cErrCh:
if err == nil {
continue
}
errCh <- E.From(err)
select {
case <-ctx.Done():
return
default:
if D.IsErrConnectionFailed(err) {
time.Sleep(100 * time.Millisecond)
cEventCh, cErrCh = cl.Events(ctx, dwOptions)
}
}
}
}
}()
<-started
return eventCh, errCh
}
var dwOptions = events.ListOptions{Filters: filters.NewArgs(
filters.Arg("type", string(events.ContainerEventType)),
filters.Arg("event", string(events.ActionStart)),
filters.Arg("event", string(events.ActionDie)), // 'stop' already triggering 'die'
)}