Files
godoxy-yusing/src/watcher/docker_watcher.go
2024-08-01 10:06:42 +08:00

87 lines
1.7 KiB
Go

package watcher
import (
"context"
"time"
"github.com/docker/docker/api/types"
"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.IsNotNil() {
break
}
errCh <- E.From(err)
time.Sleep(1 * time.Second)
}
if err.IsNotNil() {
errCh <- E.Failure("connect to docker")
return
}
cEventCh, cErrCh := cl.Events(ctx, dwOptions)
started <- struct{}{}
for {
select {
case <-ctx.Done():
errCh <- E.From(<-cErrCh)
return
case msg := <-cEventCh:
containerName, ok := msg.Actor.Attributes["name"]
if !ok {
// NOTE: should not happen
// but if it happens, just ignore it
continue
}
eventCh <- Event{
ActorName: containerName,
Action: ActionModified,
}
case err := <-cErrCh:
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 = types.EventsOptions{Filters: filters.NewArgs(
filters.Arg("type", "container"),
filters.Arg("event", "start"),
filters.Arg("event", "die"), // 'stop' already triggering 'die'
)}