2025-03-27 16:27:41 +08:00

195 lines
4.6 KiB
Go

package docker
import (
"context"
"fmt"
"io"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"gitee.ltd/lxh/wechat-robot/internal/config"
)
// ContainerInfo 容器信息
type ContainerInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
Status string `json:"status"`
Created int64 `json:"created"`
Labels map[string]string `json:"labels"`
}
// CreateContainer 创建容器
func CreateContainer(ctx context.Context, cfg *config.DockerConfig, name string, env []string, labels map[string]string) (string, error) {
cli := GetClient()
// 端口映射
portBindings := nat.PortMap{}
exposedPorts := nat.PortSet{}
// 创建挂载点
var mounts []mount.Mount
if cfg.VolumePath != "" {
mounts = append(mounts, mount.Mount{
Type: mount.TypeBind,
Source: fmt.Sprintf("%s/%s", cfg.VolumePath, name),
Target: "/data",
})
}
// 设置容器配置
containerConfig := &container.Config{
Image: cfg.ImageName,
Env: env,
ExposedPorts: exposedPorts,
Labels: labels,
}
// 设置主机配置
hostConfig := &container.HostConfig{
PortBindings: portBindings,
Mounts: mounts,
RestartPolicy: container.RestartPolicy{
Name: "unless-stopped",
},
}
// 设置网络配置
networkingConfig := &network.NetworkingConfig{}
if cfg.Network != "" {
endpointsConfig := make(map[string]*network.EndpointSettings)
endpointsConfig[cfg.Network] = &network.EndpointSettings{}
networkingConfig.EndpointsConfig = endpointsConfig
}
// 创建容器
resp, err := cli.ContainerCreate(
ctx,
containerConfig,
hostConfig,
networkingConfig,
nil, // 平台
name,
)
if err != nil {
return "", err
}
return resp.ID, nil
}
// StartContainer 启动容器
func StartContainer(ctx context.Context, containerID string) error {
cli := GetClient()
return cli.ContainerStart(ctx, containerID, types.ContainerStartOptions{})
}
// StopContainer 停止容器
func StopContainer(ctx context.Context, containerID string, timeout *time.Duration) error {
cli := GetClient()
t := int(timeout.Seconds())
return cli.ContainerStop(ctx, containerID, container.StopOptions{Timeout: &t})
}
// RemoveContainer 删除容器
func RemoveContainer(ctx context.Context, containerID string, force bool) error {
cli := GetClient()
return cli.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{
Force: force,
})
}
// ListContainers 列出容器
func ListContainers(ctx context.Context, filterArgs map[string][]string) ([]ContainerInfo, error) {
cli := GetClient()
// 构建过滤器
filterSet := filters.NewArgs()
for k, vals := range filterArgs {
for _, v := range vals {
filterSet.Add(k, v)
}
}
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{
All: true, // 包括未运行的容器
Filters: filterSet,
})
if err != nil {
return nil, err
}
var containerInfos []ContainerInfo
for _, c := range containers {
containerInfos = append(containerInfos, ContainerInfo{
ID: c.ID,
Name: c.Names[0][1:], // 去掉前面的/
Image: c.Image,
Status: c.Status,
Created: c.Created,
Labels: c.Labels,
})
}
return containerInfos, nil
}
// GetContainerLogs 获取容器日志
func GetContainerLogs(ctx context.Context, containerID string, tail string) (string, error) {
cli := GetClient()
options := types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Tail: tail,
}
logs, err := cli.ContainerLogs(ctx, containerID, options)
if err != nil {
return "", err
}
defer logs.Close()
// 读取日志内容
logContent, err := io.ReadAll(logs)
if err != nil {
return "", err
}
return string(logContent), nil
}
// GetContainerStatus 获取容器状态
func GetContainerStatus(ctx context.Context, containerID string) (string, error) {
cli := GetClient()
inspect, err := cli.ContainerInspect(ctx, containerID)
if err != nil {
return "", err
}
if inspect.State.Running {
return "running", nil
} else if inspect.State.Paused {
return "paused", nil
} else if inspect.State.Restarting {
return "restarting", nil
} else {
return "stopped", nil
}
}
// WaitForContainer 等待容器达到指定状态
func WaitForContainer(ctx context.Context, containerID string) (<-chan container.WaitResponse, <-chan error) {
cli := GetClient()
return cli.ContainerWait(ctx, containerID, container.WaitConditionNotRunning)
}