sshpoke/internal/docker/api.go

129 lines
3.2 KiB
Go

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, all bool) (dto.Container, bool) {
container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{
Filters: filters.NewArgs(filters.Arg("id", id)),
All: all,
})
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
}