Version Inicial
This commit is contained in:
238
internal/docker/docker.go
Normal file
238
internal/docker/docker.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user