2023-11-16 20:09:40 +03:00
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
|
|
|
"github.com/Neur0toxine/sshpoke/internal/logger"
|
2023-11-18 12:36:17 +03:00
|
|
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
2023-11-16 20:09:40 +03:00
|
|
|
"github.com/docker/docker/api/types"
|
2023-11-16 22:50:21 +03:00
|
|
|
"github.com/docker/docker/api/types/filters"
|
2023-11-16 20:09:40 +03:00
|
|
|
"github.com/docker/docker/client"
|
|
|
|
)
|
|
|
|
|
2023-11-16 22:50:21 +03:00
|
|
|
var Default *Docker
|
|
|
|
|
|
|
|
type Docker struct {
|
|
|
|
cli *client.Client
|
|
|
|
ctx context.Context
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(ctx context.Context) (*Docker, error) {
|
2023-11-17 20:39:00 +03:00
|
|
|
cli, err := client.NewClientWithOpts(config.Default.Docker.Opts)
|
2023-11-16 22:50:21 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &Docker{
|
|
|
|
cli: cli,
|
|
|
|
ctx: ctx,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-11-18 12:36:17 +03:00
|
|
|
func (d *Docker) Containers() map[string]dto.Container {
|
2023-11-16 22:50:21 +03:00
|
|
|
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
|
|
|
|
}
|
2023-11-18 12:36:17 +03:00
|
|
|
containers := map[string]dto.Container{}
|
2023-11-16 22:50:21 +03:00
|
|
|
for _, item := range items {
|
|
|
|
container, ok := dockerContainerToInternal(item)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
containers[item.ID] = container
|
|
|
|
}
|
|
|
|
return containers
|
|
|
|
}
|
|
|
|
|
2023-11-22 22:21:11 +03:00
|
|
|
func (d *Docker) GetContainer(id string, all bool) (dto.Container, bool) {
|
2023-11-18 16:14:39 +03:00
|
|
|
container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{
|
|
|
|
Filters: filters.NewArgs(filters.Arg("id", id)),
|
2023-11-22 22:21:11 +03:00
|
|
|
All: all,
|
2023-11-18 16:14:39 +03:00
|
|
|
})
|
|
|
|
if err != nil || len(container) != 1 {
|
|
|
|
return dto.Container{}, false
|
|
|
|
}
|
|
|
|
converted, ok := dockerContainerToInternal(container[0])
|
|
|
|
if !ok {
|
|
|
|
return dto.Container{}, false
|
|
|
|
}
|
|
|
|
return converted, true
|
|
|
|
}
|
|
|
|
|
2023-11-18 12:36:17 +03:00
|
|
|
func (d *Docker) Listen() (chan dto.Event, error) {
|
2023-11-17 20:39:00 +03:00
|
|
|
cli, err := client.NewClientWithOpts(config.Default.Docker.Opts)
|
2023-11-16 20:09:40 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-18 12:36:17 +03:00
|
|
|
output := make(chan dto.Event)
|
2023-11-16 20:09:40 +03:00
|
|
|
go func() {
|
|
|
|
for {
|
2023-11-16 22:50:21 +03:00
|
|
|
eventSource, errSource := cli.Events(d.ctx, types.EventsOptions{
|
|
|
|
Filters: filters.NewArgs(filters.Arg("type", "container")),
|
|
|
|
})
|
2023-11-16 20:09:40 +03:00
|
|
|
select {
|
|
|
|
case event := <-eventSource:
|
2023-11-18 12:36:17 +03:00
|
|
|
eventType := dto.TypeFromAction(event.Action)
|
|
|
|
if (eventType != dto.EventStart && eventType != dto.EventStop) || !actorEnabled(event.Actor) {
|
2023-11-16 22:50:21 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{
|
|
|
|
Filters: filters.NewArgs(filters.Arg("id", event.Actor.ID)),
|
2023-11-17 20:39:00 +03:00
|
|
|
All: true,
|
2023-11-16 22:50:21 +03:00
|
|
|
})
|
|
|
|
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
|
|
|
|
}
|
2023-11-18 12:36:17 +03:00
|
|
|
newEvent := dto.Event{
|
2023-11-16 22:50:21 +03:00
|
|
|
Type: eventType,
|
|
|
|
Container: converted,
|
|
|
|
}
|
2023-11-17 20:39:00 +03:00
|
|
|
msg := "exposing container"
|
2023-11-18 12:36:17 +03:00
|
|
|
if eventType == dto.EventStop {
|
2023-11-17 20:39:00 +03:00
|
|
|
msg = "stopping container"
|
|
|
|
}
|
|
|
|
logger.Sugar.Debugw(msg,
|
2023-11-16 22:50:21 +03:00
|
|
|
"type", event.Action,
|
|
|
|
"container.id", event.Actor.ID,
|
|
|
|
"container.ip", converted.IP.String(),
|
|
|
|
"container.port", converted.Port,
|
|
|
|
"container.server", converted.Server,
|
2023-11-18 21:23:29 +03:00
|
|
|
"container.remote_host", converted.RemoteHost)
|
2023-11-16 22:50:21 +03:00
|
|
|
output <- newEvent
|
2023-11-16 20:09:40 +03:00
|
|
|
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
|
|
|
|
}
|