Files
panel/internal/data/container.go
T
耗子 194287554e refactor: migrate to chi framework (#165)
* 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>
2024-09-18 01:43:14 +08:00

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
}