package docker import ( "context" "errors" "time" "github.com/Neur0toxine/sshpoke/internal/config" "github.com/Neur0toxine/sshpoke/internal/logger" "github.com/Neur0toxine/sshpoke/pkg/dto" "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]dto.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]dto.Container{} for _, item := range items { container, ok := dockerContainerToInternal(item) if !ok { continue } containers[item.ID] = container } return containers } func (d *Docker) GetContainer(id string) (dto.Container, bool) { container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{ Filters: filters.NewArgs(filters.Arg("id", id)), All: true, }) if err != nil || len(container) != 1 { return dto.Container{}, false } converted, ok := dockerContainerToInternal(container[0]) if !ok { return dto.Container{}, false } return converted, true } func (d *Docker) Listen() (chan dto.Event, error) { cli, err := client.NewClientWithOpts(config.Default.Docker.Opts) if err != nil { return nil, err } output := make(chan dto.Event) go func() { for { eventSource, errSource := cli.Events(d.ctx, types.EventsOptions{ Filters: filters.NewArgs(filters.Arg("type", "container")), }) select { case event := <-eventSource: eventType := dto.TypeFromAction(event.Action) if (eventType != dto.EventStart && eventType != dto.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 := dto.Event{ Type: eventType, Container: converted, } msg := "exposing container" if eventType == dto.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.remote_host", converted.RemoteHost) 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 }