mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 06:47:20 +08:00
feat: 使用docker v29版本客户端
This commit is contained in:
2
go.mod
2
go.mod
@@ -43,6 +43,7 @@ require (
|
||||
github.com/libtnb/sessions v1.2.1
|
||||
github.com/libtnb/utils v1.2.1
|
||||
github.com/mholt/acmez/v3 v3.1.4
|
||||
github.com/moby/moby/api v1.52.0
|
||||
github.com/moby/moby/client v0.1.0
|
||||
github.com/ncruces/go-sqlite3 v0.30.1
|
||||
github.com/ncruces/go-sqlite3/gormlite v0.30.1
|
||||
@@ -110,7 +111,6 @@ require (
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/moby/api v1.52.0 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
|
||||
@@ -3,15 +3,18 @@ package data
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
"github.com/acepanel/panel/pkg/shell"
|
||||
"github.com/acepanel/panel/pkg/types"
|
||||
)
|
||||
|
||||
@@ -89,125 +92,242 @@ func (r *containerRepo) ListByName(names string) ([]types.Container, error) {
|
||||
|
||||
// Create 创建容器
|
||||
func (r *containerRepo) Create(req *request.ContainerCreate) (string, error) {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("docker run -d --name %s", req.Name))
|
||||
if req.PublishAllPorts {
|
||||
sb.WriteString(" -P")
|
||||
} else {
|
||||
for _, port := range req.Ports {
|
||||
sb.WriteString(" -p ")
|
||||
if port.Host.IsValid() {
|
||||
sb.WriteString(fmt.Sprintf("%s:", port.Host.String()))
|
||||
}
|
||||
if port.HostStart == port.HostEnd || port.ContainerStart == port.ContainerEnd {
|
||||
sb.WriteString(fmt.Sprintf("%d:%d/%s", port.HostStart, port.ContainerStart, port.Protocol))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%d-%d:%d-%d/%s", port.HostStart, port.HostEnd, port.ContainerStart, port.ContainerEnd, port.Protocol))
|
||||
}
|
||||
}
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if req.Network != "" {
|
||||
sb.WriteString(fmt.Sprintf(" --network %s", req.Network))
|
||||
}
|
||||
for _, volume := range req.Volumes {
|
||||
sb.WriteString(fmt.Sprintf(" -v %s:%s:%s", volume.Host, volume.Container, volume.Mode))
|
||||
}
|
||||
for _, label := range req.Labels {
|
||||
sb.WriteString(fmt.Sprintf(" --label %s=%s", label.Key, label.Value))
|
||||
}
|
||||
for _, env := range req.Env {
|
||||
sb.WriteString(fmt.Sprintf(" -e %s=%s", env.Key, env.Value))
|
||||
}
|
||||
if len(req.Entrypoint) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" --entrypoint '%s'", strings.Join(req.Entrypoint, " ")))
|
||||
}
|
||||
if len(req.Command) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" '%s'", strings.Join(req.Command, " ")))
|
||||
}
|
||||
if req.RestartPolicy != "" {
|
||||
sb.WriteString(fmt.Sprintf(" --restart %s", req.RestartPolicy))
|
||||
}
|
||||
if req.AutoRemove {
|
||||
sb.WriteString(" --rm")
|
||||
}
|
||||
if req.Privileged {
|
||||
sb.WriteString(" --privileged")
|
||||
}
|
||||
if req.OpenStdin {
|
||||
sb.WriteString(" -i")
|
||||
}
|
||||
if req.Tty {
|
||||
sb.WriteString(" -t")
|
||||
}
|
||||
if req.CPUShares > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" --cpu-shares %d", req.CPUShares))
|
||||
}
|
||||
if req.CPUs > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" --cpus %d", req.CPUs))
|
||||
}
|
||||
if req.Memory > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" --memory %d", req.Memory))
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 构建容器配置
|
||||
config := &container.Config{
|
||||
Image: req.Image,
|
||||
Tty: req.Tty,
|
||||
OpenStdin: req.OpenStdin,
|
||||
AttachStdin: req.OpenStdin,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Env: types.KVToSlice(req.Env),
|
||||
Labels: types.KVToMap(req.Labels),
|
||||
Entrypoint: req.Entrypoint,
|
||||
Cmd: req.Command,
|
||||
}
|
||||
|
||||
sb.WriteString(" %s bash")
|
||||
return shell.ExecfWithTTY(sb.String(), req.Image)
|
||||
// 构建主机配置
|
||||
hostConfig := &container.HostConfig{
|
||||
AutoRemove: req.AutoRemove,
|
||||
Privileged: req.Privileged,
|
||||
PublishAllPorts: req.PublishAllPorts,
|
||||
}
|
||||
|
||||
// 构建网络配置
|
||||
networkConfig := &network.NetworkingConfig{}
|
||||
if req.Network != "" {
|
||||
switch req.Network {
|
||||
case "host", "none", "bridge":
|
||||
hostConfig.NetworkMode = container.NetworkMode(req.Network)
|
||||
}
|
||||
networkConfig.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
||||
}
|
||||
|
||||
// 设置端口映射
|
||||
if !req.PublishAllPorts && len(req.Ports) > 0 {
|
||||
portMap := make(network.PortMap)
|
||||
for _, port := range req.Ports {
|
||||
if port.ContainerStart-port.ContainerEnd != port.HostStart-port.HostEnd {
|
||||
return "", fmt.Errorf("容器端口和主机端口数量不匹配(容器: %d 主机: %d)", port.ContainerStart-port.ContainerEnd, port.HostStart-port.HostEnd)
|
||||
}
|
||||
if port.ContainerStart > port.ContainerEnd || port.HostStart > port.HostEnd || port.ContainerStart < 1 || port.HostStart < 1 {
|
||||
return "", fmt.Errorf("端口范围不正确")
|
||||
}
|
||||
|
||||
count := uint(0)
|
||||
for i := port.HostStart; i <= port.HostEnd; i++ {
|
||||
bindItem := network.PortBinding{HostIP: port.Host, HostPort: strconv.Itoa(int(i))}
|
||||
portMap[network.MustParsePort(fmt.Sprintf("%d/%s", port.ContainerStart+count, port.Protocol))] = []network.PortBinding{bindItem}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
exposed := make(network.PortSet)
|
||||
for port := range portMap {
|
||||
exposed[port] = struct{}{}
|
||||
}
|
||||
|
||||
config.ExposedPorts = exposed
|
||||
hostConfig.PortBindings = portMap
|
||||
}
|
||||
// 设置卷挂载
|
||||
volumes := make(map[string]struct{})
|
||||
for _, v := range req.Volumes {
|
||||
volumes[v.Container] = struct{}{}
|
||||
hostConfig.Binds = append(hostConfig.Binds, fmt.Sprintf("%s:%s:%s", v.Host, v.Container, v.Mode))
|
||||
}
|
||||
config.Volumes = volumes
|
||||
// 设置重启策略
|
||||
hostConfig.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
hostConfig.RestartPolicy.MaximumRetryCount = 5
|
||||
}
|
||||
// 设置资源限制
|
||||
hostConfig.CPUShares = req.CPUShares
|
||||
hostConfig.NanoCPUs = req.CPUs * 1e9
|
||||
hostConfig.Memory = req.Memory * 1024 * 1024
|
||||
hostConfig.MemorySwap = 0
|
||||
|
||||
// 创建容器
|
||||
resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
Name: req.Name,
|
||||
Config: config,
|
||||
HostConfig: hostConfig,
|
||||
NetworkingConfig: networkConfig,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 启动容器
|
||||
_, _ = apiClient.ContainerStart(ctx, resp.ID, client.ContainerStartOptions{})
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
// Remove 移除容器
|
||||
func (r *containerRepo) Remove(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker rm -f %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerRemove(context.Background(), id, client.ContainerRemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Start 启动容器
|
||||
func (r *containerRepo) Start(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker start %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerStart(context.Background(), id, client.ContainerStartOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop 停止容器
|
||||
func (r *containerRepo) Stop(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker stop %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerStop(context.Background(), id, client.ContainerStopOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart 重启容器
|
||||
func (r *containerRepo) Restart(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker restart %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerRestart(context.Background(), id, client.ContainerRestartOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Pause 暂停容器
|
||||
func (r *containerRepo) Pause(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker pause %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerPause(context.Background(), id, client.ContainerPauseOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Unpause 恢复容器
|
||||
func (r *containerRepo) Unpause(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker unpause %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerUnpause(context.Background(), id, client.ContainerUnpauseOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Kill 杀死容器
|
||||
func (r *containerRepo) Kill(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker kill %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerKill(context.Background(), id, client.ContainerKillOptions{
|
||||
Signal: "KILL",
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename 重命名容器
|
||||
func (r *containerRepo) Rename(id string, newName string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker rename %s %s", id, newName)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerRename(context.Background(), id, client.ContainerRenameOptions{
|
||||
NewName: newName,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Logs 查看容器日志
|
||||
func (r *containerRepo) Logs(id string) (string, error) {
|
||||
return shell.ExecfWithTimeout(2*time.Minute, "docker logs --tail 100 %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
reader, err := apiClient.ContainerLogs(context.Background(), id, client.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Tail: "100",
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func(reader io.ReadCloser) { _ = reader.Close() }(reader)
|
||||
|
||||
logs, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(logs), nil
|
||||
}
|
||||
|
||||
// Prune 清理未使用的容器
|
||||
func (r *containerRepo) Prune() error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker container prune -f")
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ContainerPrune(context.Background(), client.ContainerPruneOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,16 +2,17 @@ package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
"github.com/acepanel/panel/pkg/shell"
|
||||
"github.com/acepanel/panel/pkg/tools"
|
||||
"github.com/acepanel/panel/pkg/types"
|
||||
)
|
||||
@@ -59,33 +60,58 @@ func (r *containerImageRepo) List() ([]types.ContainerImage, error) {
|
||||
|
||||
// Pull 拉取镜像
|
||||
func (r *containerImageRepo) Pull(req *request.ContainerImagePull) error {
|
||||
var sb strings.Builder
|
||||
|
||||
if req.Auth {
|
||||
sb.WriteString(fmt.Sprintf("docker login -u %s -p %s", req.Username, req.Password))
|
||||
if _, err := shell.Exec(sb.String()); err != nil {
|
||||
return fmt.Errorf("login failed: %w", err)
|
||||
}
|
||||
sb.Reset()
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("docker pull %s", req.Name))
|
||||
|
||||
if _, err := shell.Exec(sb.String()); err != nil {
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
return nil
|
||||
options := client.ImagePullOptions{}
|
||||
if req.Auth {
|
||||
authConfig := registry.AuthConfig{
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
}
|
||||
encodedJSON, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
|
||||
out, err := apiClient.ImagePull(context.Background(), req.Name, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
return out.Wait(context.Background())
|
||||
}
|
||||
|
||||
// Remove 删除镜像
|
||||
func (r *containerImageRepo) Remove(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker rmi %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ImageRemove(context.Background(), id, client.ImageRemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Prune 清理未使用的镜像
|
||||
func (r *containerImageRepo) Prune() error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker image prune -f")
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.ImagePrune(context.Background(), client.ImagePruneOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ package data
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
"github.com/acepanel/panel/pkg/shell"
|
||||
"github.com/acepanel/panel/pkg/types"
|
||||
)
|
||||
|
||||
@@ -74,43 +74,93 @@ func (r *containerNetworkRepo) List() ([]types.ContainerNetwork, error) {
|
||||
|
||||
// Create 创建网络
|
||||
func (r *containerNetworkRepo) Create(req *request.ContainerNetworkCreate) (string, error) {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(fmt.Sprintf("docker network create --driver %s", req.Driver))
|
||||
sb.WriteString(fmt.Sprintf(" %s", req.Name))
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
var ipamConfigs []network.IPAMConfig
|
||||
if req.Ipv4.Enabled {
|
||||
sb.WriteString(fmt.Sprintf(" --subnet %s", req.Ipv4.Subnet))
|
||||
sb.WriteString(fmt.Sprintf(" --gateway %s", req.Ipv4.Gateway))
|
||||
if req.Ipv4.IPRange != "" {
|
||||
sb.WriteString(fmt.Sprintf(" --ip-range %s", req.Ipv4.IPRange))
|
||||
v4Subnet, err := netip.ParsePrefix(req.Ipv4.Subnet)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid ipv4 subnet: %w", err)
|
||||
}
|
||||
v4Gateway, err := netip.ParseAddr(req.Ipv4.Gateway)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid ipv4 gateway: %w", err)
|
||||
}
|
||||
v4IPRange, err := netip.ParsePrefix(req.Ipv4.IPRange)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid ipv4 ip range: %w", err)
|
||||
}
|
||||
ipamConfigs = append(ipamConfigs, network.IPAMConfig{
|
||||
Subnet: v4Subnet,
|
||||
Gateway: v4Gateway,
|
||||
IPRange: v4IPRange,
|
||||
})
|
||||
}
|
||||
if req.Ipv6.Enabled {
|
||||
sb.WriteString(fmt.Sprintf(" --subnet %s", req.Ipv6.Subnet))
|
||||
sb.WriteString(fmt.Sprintf(" --gateway %s", req.Ipv6.Gateway))
|
||||
if req.Ipv6.IPRange != "" {
|
||||
sb.WriteString(fmt.Sprintf(" --ip-range %s", req.Ipv6.IPRange))
|
||||
v6Subnet, err := netip.ParsePrefix(req.Ipv6.Subnet)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid ipv6 subnet: %w", err)
|
||||
}
|
||||
}
|
||||
for _, label := range req.Labels {
|
||||
sb.WriteString(fmt.Sprintf(" --label %s=%s", label.Key, label.Value))
|
||||
}
|
||||
for _, option := range req.Options {
|
||||
sb.WriteString(fmt.Sprintf(" --opt %s=%s", option.Key, option.Value))
|
||||
v6Gateway, err := netip.ParseAddr(req.Ipv6.Gateway)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid ipv6 gateway: %w", err)
|
||||
}
|
||||
v6IPRange, err := netip.ParsePrefix(req.Ipv6.IPRange)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid ipv6 ip range: %w", err)
|
||||
}
|
||||
ipamConfigs = append(ipamConfigs, network.IPAMConfig{
|
||||
Subnet: v6Subnet,
|
||||
Gateway: v6Gateway,
|
||||
IPRange: v6IPRange,
|
||||
})
|
||||
}
|
||||
|
||||
return shell.Exec(sb.String())
|
||||
options := client.NetworkCreateOptions{
|
||||
EnableIPv4: &req.Ipv4.Enabled,
|
||||
EnableIPv6: &req.Ipv6.Enabled,
|
||||
Driver: req.Driver,
|
||||
Options: types.KVToMap(req.Options),
|
||||
Labels: types.KVToMap(req.Labels),
|
||||
}
|
||||
if len(ipamConfigs) > 0 {
|
||||
options.IPAM = &network.IPAM{
|
||||
Config: ipamConfigs,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := apiClient.NetworkCreate(context.Background(), req.Name, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.ID, err
|
||||
}
|
||||
|
||||
// Remove 删除网络
|
||||
func (r *containerNetworkRepo) Remove(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker network rm -f %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.NetworkRemove(context.Background(), id, client.NetworkRemoveOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Prune 清理未使用的网络
|
||||
func (r *containerNetworkRepo) Prune() error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker network prune -f")
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.NetworkPrune(context.Background(), client.NetworkPruneOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
"github.com/acepanel/panel/pkg/shell"
|
||||
"github.com/acepanel/panel/pkg/tools"
|
||||
"github.com/acepanel/panel/pkg/types"
|
||||
)
|
||||
@@ -60,32 +58,47 @@ func (r *containerVolumeRepo) List() ([]types.ContainerVolume, error) {
|
||||
|
||||
// Create 创建存储卷
|
||||
func (r *containerVolumeRepo) Create(req *request.ContainerVolumeCreate) (string, error) {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("docker volume create")
|
||||
sb.WriteString(fmt.Sprintf(" %s", req.Name))
|
||||
|
||||
if req.Driver != "" {
|
||||
sb.WriteString(fmt.Sprintf(" --driver %s", req.Driver))
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, label := range req.Labels {
|
||||
sb.WriteString(fmt.Sprintf(" --label %s=%s", label.Key, label.Value))
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
resp, err := apiClient.VolumeCreate(context.Background(), client.VolumeCreateOptions{
|
||||
Name: req.Name,
|
||||
Driver: req.Driver,
|
||||
DriverOpts: types.KVToMap(req.Options),
|
||||
Labels: types.KVToMap(req.Labels),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, option := range req.Options {
|
||||
sb.WriteString(fmt.Sprintf(" --opt %s=%s", option.Key, option.Value))
|
||||
}
|
||||
|
||||
return shell.Exec(sb.String())
|
||||
return resp.Volume.Name, nil
|
||||
}
|
||||
|
||||
// Remove 删除存储卷
|
||||
func (r *containerVolumeRepo) Remove(id string) error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker volume rm -f %s", id)
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.VolumeRemove(context.Background(), id, client.VolumeRemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Prune 清理未使用的存储卷
|
||||
func (r *containerVolumeRepo) Prune() error {
|
||||
_, err := shell.ExecfWithTimeout(2*time.Minute, "docker volume prune -f")
|
||||
apiClient, err := getDockerClient("/var/run/docker.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(apiClient *client.Client) { _ = apiClient.Close() }(apiClient)
|
||||
|
||||
_, err = apiClient.VolumePrune(context.Background(), client.VolumePruneOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/acepanel/panel/pkg/punycode"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/chix"
|
||||
"github.com/libtnb/sessions"
|
||||
|
||||
"github.com/acepanel/panel/pkg/punycode"
|
||||
)
|
||||
|
||||
// Entrance 确保通过正确的入口访问
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/acepanel/panel/internal/app"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/httplog/v3"
|
||||
@@ -16,6 +15,7 @@ import (
|
||||
sessionmiddleware "github.com/libtnb/sessions/middleware"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
||||
"github.com/acepanel/panel/internal/app"
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user