sshpoke/internal/docker/api.go

119 lines
3.0 KiB
Go

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.DefaultConfig.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.ContainerEvent, error) {
cli, err := client.NewClientWithOpts(config.DefaultConfig.Docker.Opts)
if err != nil {
return nil, err
}
output := make(chan model.ContainerEvent)
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
}
if eventType == model.EventStop {
logger.Sugar.Debugw("stopping session",
"type", event.Action, "container.id", event.Actor.ID)
output <- model.ContainerEvent{
Type: eventType,
ID: event.Actor.ID,
}
continue
}
container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{
Filters: filters.NewArgs(filters.Arg("id", event.Actor.ID)),
})
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.ContainerEvent{
Type: eventType,
ID: event.Actor.ID,
Container: converted,
}
logger.Sugar.Debugw("exposing container",
"type", event.Action,
"container.id", event.Actor.ID,
"container.ip", converted.IP.String(),
"container.port", converted.Port,
"container.server", converted.Server,
"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
}