195 lines
4.6 KiB
Go
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)
|
|
}
|