mirror of
https://github.com/tnb-labs/panel.git
synced 2026-09-01 14:55:12 +08:00
194287554e
* refactor: 重构部分完成 * fix: 添加.gitkeep * fix: build * fix: lint * fix: lint * chore(deps): Update module github.com/go-playground/validator/v10 to v10.22.1 (#162) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update module gorm.io/gorm to v1.25.12 (#161) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update module golang.org/x/net to v0.29.0 (#159) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * workflow: 更新工作流 * workflow: test new download * feat: merge frontend project * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: fix frontend build * workflow: update to ubuntu-24.04 * workflow: rename build-* * workflow: 修改fetch-depth * chore(deps): Update dependency eslint to v9 (#164) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(frontend): update dependences * chore(frontend): fix lint * chore(frontend): fix lint * workflow: add govulncheck * workflow: disable nilaway * feat: 使用新的压缩解压库 * fix: 测试 * fix: 测试 * fix: 测试 * feat: 添加ntp包 * chore(deps): Lock file maintenance (#168) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update module github.com/go-resty/resty/v2 to v2.15.0 (#167) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update dependency @iconify/json to v2.2.249 (#169) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat: 添加限流器 * feat: 调整登录限流 * feat: 证书 * fix: lint * feat: 证书dns * feat: 证书acme账号 * fix: 修改UserID导致的一系列问题 * feat: 低配版任务队列 * feat: 队列完成 * fix: lint * fix: lint * fix: swagger和前端路由 * fix: 去掉ntp测试 * feat: 完成插件接口 * feat: 完成cron * feat: 完成safe * chore(deps): Update dependency vue to v3.5.6 (#170) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update dependency @vueuse/core to v11.1.0 (#171) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update dependency vite to v5.4.6 (#173) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): Update unocss monorepo to v0.62.4 (#172) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore: update renovate config * feat: 新的firewall客户端 * fix: lint * feat: firewall完成 * feat: ssh完成 * feat: 容器完成1/2 * feat: 容器完成 * feat: 文件完成 * feat: systemctl及设置 * fix: windows编译 * fix: session not work * fix: migrate not work * feat: 前端路由 * feat: 初步支持cli --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
236 lines
6.6 KiB
Go
236 lines
6.6 KiB
Go
package data
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
|
|
"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/network"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/go-connections/nat"
|
|
|
|
"github.com/TheTNB/panel/internal/biz"
|
|
"github.com/TheTNB/panel/internal/http/request"
|
|
paneltypes "github.com/TheTNB/panel/pkg/types"
|
|
)
|
|
|
|
type containerRepo struct {
|
|
client *client.Client
|
|
}
|
|
|
|
func NewContainerRepo(sock ...string) biz.ContainerRepo {
|
|
if len(sock) == 0 {
|
|
sock = append(sock, "/run/podman/podman.sock")
|
|
}
|
|
cli, _ := client.NewClientWithOpts(client.WithHost("unix://"+sock[0]), client.WithAPIVersionNegotiation())
|
|
return &containerRepo{
|
|
client: cli,
|
|
}
|
|
}
|
|
|
|
// ListAll 列出所有容器
|
|
func (r *containerRepo) ListAll() ([]types.Container, error) {
|
|
containers, err := r.client.ContainerList(context.Background(), container.ListOptions{
|
|
All: true,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return containers, nil
|
|
}
|
|
|
|
// ListByNames 根据名称列出容器
|
|
func (r *containerRepo) ListByNames(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
|
|
}
|
|
|
|
// Create 创建容器
|
|
func (r *containerRepo) Create(req *request.ContainerCreate) (string, error) {
|
|
var hostConf container.HostConfig
|
|
var networkConf network.NetworkingConfig
|
|
|
|
portMap := make(nat.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 := 0
|
|
for host := port.HostStart; host <= port.HostEnd; host++ {
|
|
bindItem := nat.PortBinding{HostPort: strconv.Itoa(host), HostIP: port.Host}
|
|
portMap[nat.Port(fmt.Sprintf("%d/%s", port.ContainerStart+count, port.Protocol))] = []nat.PortBinding{bindItem}
|
|
count++
|
|
}
|
|
}
|
|
|
|
exposed := make(nat.PortSet)
|
|
for port := range portMap {
|
|
exposed[port] = struct{}{}
|
|
}
|
|
|
|
if req.Network != "" {
|
|
switch req.Network {
|
|
case "host", "none", "bridge":
|
|
hostConf.NetworkMode = container.NetworkMode(req.Network)
|
|
}
|
|
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
|
|
} else {
|
|
networkConf = network.NetworkingConfig{}
|
|
}
|
|
|
|
hostConf.Privileged = req.Privileged
|
|
hostConf.AutoRemove = req.AutoRemove
|
|
hostConf.CPUShares = req.CPUShares
|
|
hostConf.PublishAllPorts = req.PublishAllPorts
|
|
hostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}
|
|
if req.RestartPolicy == "on-failure" {
|
|
hostConf.RestartPolicy.MaximumRetryCount = 5
|
|
}
|
|
hostConf.NanoCPUs = req.CPUs * 1000000000
|
|
hostConf.Memory = req.Memory * 1024 * 1024
|
|
hostConf.MemorySwap = 0
|
|
hostConf.PortBindings = portMap
|
|
hostConf.Binds = []string{}
|
|
|
|
volumes := make(map[string]struct{})
|
|
for _, v := range req.Volumes {
|
|
volumes[v.Container] = struct{}{}
|
|
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", v.Host, v.Container, v.Mode))
|
|
}
|
|
|
|
resp, err := r.client.ContainerCreate(context.Background(), &container.Config{
|
|
Image: req.Image,
|
|
Env: paneltypes.KVToSlice(req.Env),
|
|
Entrypoint: req.Entrypoint,
|
|
Cmd: req.Command,
|
|
Labels: paneltypes.KVToMap(req.Labels),
|
|
ExposedPorts: exposed,
|
|
OpenStdin: req.OpenStdin,
|
|
Tty: req.Tty,
|
|
Volumes: volumes,
|
|
}, &hostConf, &networkConf, nil, req.Name)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return resp.ID, err
|
|
}
|
|
|
|
// Remove 移除容器
|
|
func (r *containerRepo) Remove(id string) error {
|
|
return r.client.ContainerRemove(context.Background(), id, container.RemoveOptions{
|
|
Force: true,
|
|
})
|
|
}
|
|
|
|
// Start 启动容器
|
|
func (r *containerRepo) Start(id string) error {
|
|
return r.client.ContainerStart(context.Background(), id, container.StartOptions{})
|
|
}
|
|
|
|
// Stop 停止容器
|
|
func (r *containerRepo) Stop(id string) error {
|
|
return r.client.ContainerStop(context.Background(), id, container.StopOptions{})
|
|
}
|
|
|
|
// Restart 重启容器
|
|
func (r *containerRepo) Restart(id string) error {
|
|
return r.client.ContainerRestart(context.Background(), id, container.StopOptions{})
|
|
}
|
|
|
|
// Pause 暂停容器
|
|
func (r *containerRepo) Pause(id string) error {
|
|
return r.client.ContainerPause(context.Background(), id)
|
|
}
|
|
|
|
// Unpause 恢复容器
|
|
func (r *containerRepo) Unpause(id string) error {
|
|
return r.client.ContainerUnpause(context.Background(), id)
|
|
}
|
|
|
|
// Inspect 查看容器
|
|
func (r *containerRepo) Inspect(id string) (types.ContainerJSON, error) {
|
|
return r.client.ContainerInspect(context.Background(), id)
|
|
}
|
|
|
|
// Kill 杀死容器
|
|
func (r *containerRepo) Kill(id string) error {
|
|
return r.client.ContainerKill(context.Background(), id, "KILL")
|
|
}
|
|
|
|
// Rename 重命名容器
|
|
func (r *containerRepo) Rename(id string, newName string) error {
|
|
return r.client.ContainerRename(context.Background(), id, newName)
|
|
}
|
|
|
|
// Stats 查看容器状态
|
|
func (r *containerRepo) Stats(id string) (container.StatsResponseReader, error) {
|
|
return r.client.ContainerStats(context.Background(), id, false)
|
|
}
|
|
|
|
// Exist 判断容器是否存在
|
|
func (r *containerRepo) Exist(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
|
|
}
|
|
|
|
// Update 更新容器
|
|
func (r *containerRepo) Update(id string, config container.UpdateConfig) error {
|
|
_, err := r.client.ContainerUpdate(context.Background(), id, config)
|
|
return err
|
|
}
|
|
|
|
// Logs 查看容器日志
|
|
func (r *containerRepo) Logs(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
|
|
}
|
|
|
|
// Prune 清理未使用的容器
|
|
func (r *containerRepo) Prune() error {
|
|
_, err := r.client.ContainersPrune(context.Background(), filters.NewArgs())
|
|
return err
|
|
}
|