package docker import ( "context" "errors" "time" "github.com/Neur0toxine/sshpoke/internal/config" "github.com/Neur0toxine/sshpoke/internal/logger" "github.com/Neur0toxine/sshpoke/internal/model" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" ) var Default *Docker type Docker struct { cli *client.Client ctx context.Context } func New(ctx context.Context) (*Docker, error) { cli, err := client.NewClientWithOpts(config.Default.Docker.Opts) if err != nil { return nil, err } return &Docker{ cli: cli, ctx: ctx, }, nil } func (d *Docker) Containers() map[string]model.Container { items, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{ Filters: filters.NewArgs(filters.Arg("status", "running")), }) if err != nil { logger.Sugar.Errorf("cannot get containers list: %s", err) return nil } containers := map[string]model.Container{} for _, item := range items { container, ok := dockerContainerToInternal(item) if !ok { continue } containers[item.ID] = container } return containers } func (d *Docker) Listen() (chan model.Event, error) { cli, err := client.NewClientWithOpts(config.Default.Docker.Opts) if err != nil { return nil, err } output := make(chan model.Event) go func() { for { eventSource, errSource := cli.Events(d.ctx, types.EventsOptions{ Filters: filters.NewArgs(filters.Arg("type", "container")), }) select { case event := <-eventSource: eventType := model.TypeFromAction(event.Action) if (eventType != model.EventStart && eventType != model.EventStop) || !actorEnabled(event.Actor) { continue } container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{ Filters: filters.NewArgs(filters.Arg("id", event.Actor.ID)), All: true, }) if err != nil || len(container) != 1 { logger.Sugar.Errorw("cannot get container info", "id", event.Actor.ID, "err", err) continue } converted, ok := dockerContainerToInternal(container[0]) if !ok { continue } newEvent := model.Event{ Type: eventType, ID: event.Actor.ID, Container: converted, } msg := "exposing container" if eventType == model.EventStop { msg = "stopping container" } logger.Sugar.Debugw(msg, "type", event.Action, "container.id", event.Actor.ID, "container.ip", converted.IP.String(), "container.port", converted.Port, "container.server", converted.Server, "container.prefix", converted.Prefix, "container.domain", converted.Domain) output <- newEvent case err := <-errSource: if errors.Is(err, context.Canceled) { logger.Sugar.Debug("stopping docker event listener...") return } logger.Sugar.Errorf("docker error, reconnect in 1s: %s", err) time.Sleep(time.Second) continue } } }() return output, nil }