Version Inicial

This commit is contained in:
2026-04-13 21:42:04 +02:00
commit f5dc96eee5
24 changed files with 2630 additions and 0 deletions

238
internal/docker/docker.go Normal file
View File

@@ -0,0 +1,238 @@
package docker
import (
"bytes"
"context"
"fmt"
"io"
"strconv"
"strings"
"time"
"fergalla.com/dockerbot/internal/apperrors"
"github.com/containerd/errdefs"
"github.com/moby/moby/api/pkg/stdcopy"
"github.com/moby/moby/client"
)
type DockerInfo struct {
ctxDocker context.Context
dockerClient *client.Client
}
/* type ContainerStats struct {
CPUPercent float64
MemUsage uint64
MemLimit uint64
MemPercent float64
NetInput uint64
NetOutput uint64
}
type statsResponse struct {
CPUStats struct {
CPUUsage struct {
TotalUsage uint64 `json:"total_usage"`
PercpuUsage []uint64 `json:"percpu_usage"`
} `json:"cpu_usage"`
SystemUsage uint64 `json:"system_cpu_usage"`
} `json:"cpu_stats"`
PreCPUStats struct {
CPUUsage struct {
TotalUsage uint64 `json:"total_usage"`
} `json:"cpu_usage"`
SystemUsage uint64 `json:"system_cpu_usage"`
} `json:"precpu_stats"`
MemoryStats struct {
Usage uint64 `json:"usage"`
Limit uint64 `json:"limit"`
} `json:"memory_stats"`
Networks map[string]struct {
RxBytes uint64 `json:"rx_bytes"`
TxBytes uint64 `json:"tx_bytes"`
} `json:"networks"`
} */
type ContainerInspectInfo struct {
ID string
Name string
Image string
Created time.Time
Path string
Command string
State string
Health string
RestartCount int
StartedAt time.Time
FinishedAt time.Time
Ports map[string]string
Mounts []string
Networks []string
}
type ContainerBasicInfo struct {
Name string
Image string
State string
Health string
}
func New() (*DockerInfo, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
apiClient, err := client.New(client.FromEnv)
if err != nil {
return nil, err
}
return &DockerInfo{
dockerClient: apiClient,
ctxDocker: ctx,
}, nil
}
func (d *DockerInfo) ListContainers() ([]ContainerBasicInfo, error) {
containers, err := d.dockerClient.ContainerList(d.ctxDocker, client.ContainerListOptions{All: true})
if err != nil {
return nil, err
}
var basicInfo []ContainerBasicInfo
for _, c := range containers.Items {
name := "unknown"
if len(c.Names) > 0 {
name = c.Names[0][1:]
}
image := c.Image
state := c.State
health := c.Health.Status
basicInfo = append(basicInfo, ContainerBasicInfo{
Name: name,
Image: image,
State: string(state),
Health: string(health),
})
}
return basicInfo, nil
}
func (d *DockerInfo) GetContainerInfo(containerID string) (*ContainerInspectInfo, error) {
containerJSON, err := d.dockerClient.ContainerInspect(d.ctxDocker, containerID, client.ContainerInspectOptions{})
if err != nil {
if errdefs.IsNotFound(err) {
return nil, apperrors.ErrContainerNotFound
}
return nil, err
}
c := containerJSON.Container
info := &ContainerInspectInfo{
Name: strings.TrimPrefix(c.Name, "/"),
Image: c.Config.Image,
ID: c.ID,
Path: c.Path,
Command: c.Path + " " + strings.Join(c.Args, " "),
State: string(c.State.Status),
Health: "none",
RestartCount: c.RestartCount,
Created: strToTime(c.Created),
StartedAt: strToTime(c.State.StartedAt),
FinishedAt: strToTime(c.State.FinishedAt),
}
if c.State.Health != nil {
info.Health = string(c.State.Health.Status)
}
info.Ports = make(map[string]string)
for port, bindings := range c.NetworkSettings.Ports {
if len(bindings) > 0 {
info.Ports[port.String()] = bindings[0].HostPort
}
}
for _, m := range c.Mounts {
info.Mounts = append(info.Mounts, m.Source+"->"+m.Destination)
}
for name := range c.NetworkSettings.Networks {
info.Networks = append(info.Networks, name)
}
return info, nil
}
func (d *DockerInfo) ContainerAction(containerID string, action string) error {
var err error
switch action {
case "start":
_, err = d.dockerClient.ContainerStart(d.ctxDocker, containerID, client.ContainerStartOptions{})
case "stop":
_, err = d.dockerClient.ContainerStop(d.ctxDocker, containerID, client.ContainerStopOptions{})
case "restart":
_, err = d.dockerClient.ContainerRestart(d.ctxDocker, containerID, client.ContainerRestartOptions{})
case "pause":
_, err = d.dockerClient.ContainerPause(d.ctxDocker, containerID, client.ContainerPauseOptions{})
case "unpause":
_, err = d.dockerClient.ContainerUnpause(d.ctxDocker, containerID, client.ContainerUnpauseOptions{})
default:
return fmt.Errorf("unknown docker action: %s", action)
}
if errdefs.IsNotFound(err) {
return apperrors.ErrContainerNotFound
} else {
return err
}
}
func (d *DockerInfo) GetContainerLogs(containerID string, tail int) (string, error) {
opts := client.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
}
if tail != 0 {
opts.Tail = strconv.Itoa(tail)
}
reader, err := d.dockerClient.ContainerLogs(d.ctxDocker, containerID, opts)
if err != nil {
if errdefs.IsNotFound(err) {
return "", apperrors.ErrContainerNotFound
} else {
return "", err
}
}
defer reader.Close()
var stdoutBuf bytes.Buffer
var stderrBuf bytes.Buffer
_, err = stdcopy.StdCopy(&stdoutBuf, &stderrBuf, reader)
if err != nil && err != io.EOF {
return "", err
}
out := "\n\n⚠ STDOUT:\n" + stdoutBuf.String()
errOut := stderrBuf.String()
if errOut != "" {
out += "\n\n⚠ STDERR:\n" + errOut
}
return out, nil
}
func strToTime(date string) time.Time {
t, err := time.Parse(time.RFC3339Nano, date)
if err != nil {
t = time.Time{}
}
return t
}