mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 09:13:49 +08:00
375 lines
10 KiB
Go
375 lines
10 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"io"
|
|
|
|
"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/image"
|
|
"github.com/docker/docker/api/types/network"
|
|
"github.com/docker/docker/api/types/registry"
|
|
"github.com/docker/docker/api/types/volume"
|
|
"github.com/docker/docker/client"
|
|
"github.com/goravel/framework/support/json"
|
|
|
|
requests "github.com/TheTNB/panel/app/http/requests/container"
|
|
paneltypes "github.com/TheTNB/panel/pkg/types"
|
|
)
|
|
|
|
type Container struct {
|
|
client *client.Client
|
|
}
|
|
|
|
func NewContainer(sock ...string) Container {
|
|
if len(sock) == 0 {
|
|
sock = append(sock, "/run/podman/podman.sock")
|
|
}
|
|
cli, _ := client.NewClientWithOpts(client.WithHost("unix://"+sock[0]), client.WithAPIVersionNegotiation())
|
|
return Container{
|
|
client: cli,
|
|
}
|
|
}
|
|
|
|
// ContainerListAll 列出所有容器
|
|
func (r *Container) ContainerListAll() ([]types.Container, error) {
|
|
containers, err := r.client.ContainerList(context.Background(), container.ListOptions{
|
|
All: true,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return containers, nil
|
|
}
|
|
|
|
// ContainerListByNames 根据名称列出容器
|
|
func (r *Container) ContainerListByNames(names []string) ([]types.Container, error) {
|
|
var options container.ListOptions
|
|
options.All = true
|
|
if len(names) > 0 {
|
|
var array []filters.KeyValuePair
|
|
for _, n := range names {
|
|
array = append(array, filters.Arg("name", n))
|
|
}
|
|
options.Filters = filters.NewArgs(array...)
|
|
}
|
|
containers, err := r.client.ContainerList(context.Background(), options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return containers, nil
|
|
}
|
|
|
|
// ContainerCreate 创建容器
|
|
func (r *Container) ContainerCreate(name string, config container.Config, host container.HostConfig, network network.NetworkingConfig) (string, error) {
|
|
resp, err := r.client.ContainerCreate(context.Background(), &config, &host, &network, nil, name)
|
|
return resp.ID, err
|
|
}
|
|
|
|
// ContainerRemove 移除容器
|
|
func (r *Container) ContainerRemove(id string) error {
|
|
return r.client.ContainerRemove(context.Background(), id, container.RemoveOptions{
|
|
Force: true,
|
|
})
|
|
}
|
|
|
|
// ContainerStart 启动容器
|
|
func (r *Container) ContainerStart(id string) error {
|
|
return r.client.ContainerStart(context.Background(), id, container.StartOptions{})
|
|
}
|
|
|
|
// ContainerStop 停止容器
|
|
func (r *Container) ContainerStop(id string) error {
|
|
return r.client.ContainerStop(context.Background(), id, container.StopOptions{})
|
|
}
|
|
|
|
// ContainerRestart 重启容器
|
|
func (r *Container) ContainerRestart(id string) error {
|
|
return r.client.ContainerRestart(context.Background(), id, container.StopOptions{})
|
|
}
|
|
|
|
// ContainerPause 暂停容器
|
|
func (r *Container) ContainerPause(id string) error {
|
|
return r.client.ContainerPause(context.Background(), id)
|
|
}
|
|
|
|
// ContainerUnpause 恢复容器
|
|
func (r *Container) ContainerUnpause(id string) error {
|
|
return r.client.ContainerUnpause(context.Background(), id)
|
|
}
|
|
|
|
// ContainerInspect 查看容器
|
|
func (r *Container) ContainerInspect(id string) (types.ContainerJSON, error) {
|
|
return r.client.ContainerInspect(context.Background(), id)
|
|
}
|
|
|
|
// ContainerKill 杀死容器
|
|
func (r *Container) ContainerKill(id string) error {
|
|
return r.client.ContainerKill(context.Background(), id, "KILL")
|
|
}
|
|
|
|
// ContainerRename 重命名容器
|
|
func (r *Container) ContainerRename(id string, newName string) error {
|
|
return r.client.ContainerRename(context.Background(), id, newName)
|
|
}
|
|
|
|
// ContainerStats 查看容器状态
|
|
func (r *Container) ContainerStats(id string) (types.ContainerStats, error) {
|
|
return r.client.ContainerStats(context.Background(), id, false)
|
|
}
|
|
|
|
// ContainerExist 判断容器是否存在
|
|
func (r *Container) ContainerExist(name string) (bool, error) {
|
|
var options container.ListOptions
|
|
options.Filters = filters.NewArgs(filters.Arg("name", name))
|
|
containers, err := r.client.ContainerList(context.Background(), options)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return len(containers) > 0, nil
|
|
}
|
|
|
|
// ContainerUpdate 更新容器
|
|
func (r *Container) ContainerUpdate(id string, config container.UpdateConfig) error {
|
|
_, err := r.client.ContainerUpdate(context.Background(), id, config)
|
|
return err
|
|
}
|
|
|
|
// ContainerLogs 查看容器日志
|
|
func (r *Container) ContainerLogs(id string) (string, error) {
|
|
options := container.LogsOptions{
|
|
ShowStdout: true,
|
|
ShowStderr: true,
|
|
}
|
|
reader, err := r.client.ContainerLogs(context.Background(), id, options)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer reader.Close()
|
|
|
|
data, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|
|
|
|
// ContainerPrune 清理未使用的容器
|
|
func (r *Container) ContainerPrune() error {
|
|
_, err := r.client.ContainersPrune(context.Background(), filters.NewArgs())
|
|
return err
|
|
}
|
|
|
|
// NetworkList 列出网络
|
|
func (r *Container) NetworkList() ([]types.NetworkResource, error) {
|
|
return r.client.NetworkList(context.Background(), types.NetworkListOptions{})
|
|
}
|
|
|
|
// NetworkCreate 创建网络
|
|
func (r *Container) NetworkCreate(config requests.NetworkCreate) (string, error) {
|
|
var ipamConfigs []network.IPAMConfig
|
|
if config.Ipv4.Enabled {
|
|
ipamConfigs = append(ipamConfigs, network.IPAMConfig{
|
|
Subnet: config.Ipv4.Subnet,
|
|
Gateway: config.Ipv4.Gateway,
|
|
IPRange: config.Ipv4.IPRange,
|
|
})
|
|
}
|
|
if config.Ipv6.Enabled {
|
|
ipamConfigs = append(ipamConfigs, network.IPAMConfig{
|
|
Subnet: config.Ipv6.Subnet,
|
|
Gateway: config.Ipv6.Gateway,
|
|
IPRange: config.Ipv6.IPRange,
|
|
})
|
|
}
|
|
|
|
options := types.NetworkCreate{
|
|
EnableIPv6: config.Ipv6.Enabled,
|
|
Driver: config.Driver,
|
|
Options: r.KVToMap(config.Options),
|
|
Labels: r.KVToMap(config.Labels),
|
|
}
|
|
if len(ipamConfigs) > 0 {
|
|
options.IPAM = &network.IPAM{
|
|
Config: ipamConfigs,
|
|
}
|
|
}
|
|
|
|
resp, err := r.client.NetworkCreate(context.Background(), config.Name, options)
|
|
return resp.ID, err
|
|
}
|
|
|
|
// NetworkRemove 删除网络
|
|
func (r *Container) NetworkRemove(id string) error {
|
|
return r.client.NetworkRemove(context.Background(), id)
|
|
}
|
|
|
|
// NetworkExist 判断网络是否存在
|
|
func (r *Container) NetworkExist(name string) (bool, error) {
|
|
var options types.NetworkListOptions
|
|
options.Filters = filters.NewArgs(filters.Arg("name", name))
|
|
networks, err := r.client.NetworkList(context.Background(), options)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return len(networks) > 0, nil
|
|
}
|
|
|
|
// NetworkInspect 查看网络
|
|
func (r *Container) NetworkInspect(id string) (types.NetworkResource, error) {
|
|
return r.client.NetworkInspect(context.Background(), id, types.NetworkInspectOptions{})
|
|
}
|
|
|
|
// NetworkConnect 连接网络
|
|
func (r *Container) NetworkConnect(networkID string, containerID string) error {
|
|
return r.client.NetworkConnect(context.Background(), networkID, containerID, nil)
|
|
}
|
|
|
|
// NetworkDisconnect 断开网络
|
|
func (r *Container) NetworkDisconnect(networkID string, containerID string) error {
|
|
return r.client.NetworkDisconnect(context.Background(), networkID, containerID, true)
|
|
}
|
|
|
|
// NetworkPrune 清理未使用的网络
|
|
func (r *Container) NetworkPrune() error {
|
|
_, err := r.client.NetworksPrune(context.Background(), filters.NewArgs())
|
|
return err
|
|
}
|
|
|
|
// ImageList 列出镜像
|
|
func (r *Container) ImageList() ([]image.Summary, error) {
|
|
return r.client.ImageList(context.Background(), image.ListOptions{
|
|
All: true,
|
|
})
|
|
}
|
|
|
|
// ImageExist 判断镜像是否存在
|
|
func (r *Container) ImageExist(id string) (bool, error) {
|
|
var options image.ListOptions
|
|
options.Filters = filters.NewArgs(filters.Arg("reference", id))
|
|
images, err := r.client.ImageList(context.Background(), options)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return len(images) > 0, nil
|
|
}
|
|
|
|
// ImagePull 拉取镜像
|
|
func (r *Container) ImagePull(config requests.ImagePull) error {
|
|
options := image.PullOptions{}
|
|
if config.Auth {
|
|
authConfig := registry.AuthConfig{
|
|
Username: config.Username,
|
|
Password: config.Password,
|
|
}
|
|
encodedJSON, err := json.Marshal(authConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
|
options.RegistryAuth = authStr
|
|
}
|
|
|
|
out, err := r.client.ImagePull(context.Background(), config.Name, options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
_, err = io.Copy(io.Discard, out)
|
|
return err
|
|
}
|
|
|
|
// ImageRemove 删除镜像
|
|
func (r *Container) ImageRemove(id string) error {
|
|
_, err := r.client.ImageRemove(context.Background(), id, image.RemoveOptions{
|
|
Force: true,
|
|
PruneChildren: true,
|
|
})
|
|
return err
|
|
}
|
|
|
|
// ImagePrune 清理未使用的镜像
|
|
func (r *Container) ImagePrune() error {
|
|
_, err := r.client.ImagesPrune(context.Background(), filters.NewArgs())
|
|
return err
|
|
}
|
|
|
|
// ImageInspect 查看镜像
|
|
func (r *Container) ImageInspect(id string) (types.ImageInspect, error) {
|
|
img, _, err := r.client.ImageInspectWithRaw(context.Background(), id)
|
|
return img, err
|
|
}
|
|
|
|
// VolumeList 列出存储卷
|
|
func (r *Container) VolumeList() ([]*volume.Volume, error) {
|
|
volumes, err := r.client.VolumeList(context.Background(), volume.ListOptions{})
|
|
return volumes.Volumes, err
|
|
}
|
|
|
|
// VolumeCreate 创建存储卷
|
|
func (r *Container) VolumeCreate(config requests.VolumeCreate) (volume.Volume, error) {
|
|
return r.client.VolumeCreate(context.Background(), volume.CreateOptions{
|
|
Name: config.Name,
|
|
Driver: config.Driver,
|
|
DriverOpts: r.KVToMap(config.Options),
|
|
Labels: r.KVToMap(config.Labels),
|
|
})
|
|
}
|
|
|
|
// VolumeExist 判断存储卷是否存在
|
|
func (r *Container) VolumeExist(id string) (bool, error) {
|
|
var options volume.ListOptions
|
|
options.Filters = filters.NewArgs(filters.Arg("name", id))
|
|
volumes, err := r.client.VolumeList(context.Background(), options)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return len(volumes.Volumes) > 0, nil
|
|
}
|
|
|
|
// VolumeInspect 查看存储卷
|
|
func (r *Container) VolumeInspect(id string) (volume.Volume, error) {
|
|
return r.client.VolumeInspect(context.Background(), id)
|
|
}
|
|
|
|
// VolumeRemove 删除存储卷
|
|
func (r *Container) VolumeRemove(id string) error {
|
|
return r.client.VolumeRemove(context.Background(), id, true)
|
|
}
|
|
|
|
// VolumePrune 清理未使用的存储卷
|
|
func (r *Container) VolumePrune() error {
|
|
_, err := r.client.VolumesPrune(context.Background(), filters.NewArgs())
|
|
return err
|
|
}
|
|
|
|
// KVToMap 将 key-value 切片转换为 map
|
|
func (r *Container) KVToMap(kvs []paneltypes.KV) map[string]string {
|
|
m := make(map[string]string)
|
|
for _, item := range kvs {
|
|
m[item.Key] = item.Value
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
// KVToSlice 将 key-value 切片转换为 key=value 切片
|
|
func (r *Container) KVToSlice(kvs []paneltypes.KV) []string {
|
|
var s []string
|
|
for _, item := range kvs {
|
|
s = append(s, item.Key+"="+item.Value)
|
|
}
|
|
|
|
return s
|
|
}
|