package service import ( "bytes" "context" "encoding/json" "fmt" "io" "strings" "time" "github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/utils/docker" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/volume" "github.com/docker/docker/pkg/stdcopy" ) type ContainerService struct{} type IContainerService interface { Page(req dto.PageContainer) (int64, interface{}, error) PageNetwork(req dto.PageInfo) (int64, interface{}, error) PageVolume(req dto.PageInfo) (int64, interface{}, error) ContainerOperation(req dto.ContainerOperation) error ContainerLogs(param dto.ContainerLog) (string, error) Inspect(req dto.InspectReq) (string, error) DeleteNetwork(req dto.BatchDelete) error CreateNetwork(req dto.NetworkCreat) error DeleteVolume(req dto.BatchDelete) error CreateVolume(req dto.VolumeCreat) error } func NewIContainerService() IContainerService { return &ContainerService{} } func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) { var ( records []types.Container list []types.Container backDatas []dto.ContainerInfo ) client, err := docker.NewDockerClient() if err != nil { return 0, nil, err } list, err = client.ContainerList(context.Background(), types.ContainerListOptions{All: req.Status == "all"}) if err != nil { return 0, nil, err } total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize if start > total { records = make([]types.Container, 0) } else { if end >= total { end = total } records = list[start:end] } for _, container := range records { backDatas = append(backDatas, dto.ContainerInfo{ ContainerID: container.ID, CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"), Name: container.Names[0][1:], ImageId: strings.Split(container.ImageID, ":")[1], ImageName: container.Image, State: container.State, RunTime: container.Status, }) } return int64(total), backDatas, nil } func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) { client, err := docker.NewDockerClient() if err != nil { return "", err } var inspectInfo interface{} switch req.Type { case "container": inspectInfo, err = client.ContainerInspect(context.Background(), req.ID) case "network": inspectInfo, err = client.NetworkInspect(context.TODO(), req.ID, types.NetworkInspectOptions{}) case "volume": inspectInfo, err = client.VolumeInspect(context.TODO(), req.ID) } if err != nil { return "", err } bytes, err := json.Marshal(inspectInfo) if err != nil { return "", err } return string(bytes), nil } func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error { var err error ctx := context.Background() dc, err := docker.NewDockerClient() if err != nil { return err } switch req.Operation { case constant.ContainerOpStart: err = dc.ContainerStart(ctx, req.ContainerID, types.ContainerStartOptions{}) case constant.ContainerOpStop: err = dc.ContainerStop(ctx, req.ContainerID, nil) case constant.ContainerOpRestart: err = dc.ContainerRestart(ctx, req.ContainerID, nil) case constant.ContainerOpKill: err = dc.ContainerKill(ctx, req.ContainerID, "SIGKILL") case constant.ContainerOpPause: err = dc.ContainerPause(ctx, req.ContainerID) case constant.ContainerOpUnpause: err = dc.ContainerUnpause(ctx, req.ContainerID) case constant.ContainerOpRename: err = dc.ContainerRename(ctx, req.ContainerID, req.NewName) case constant.ContainerOpRemove: err = dc.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{RemoveVolumes: true, RemoveLinks: true, Force: true}) } return err } func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) { var ( options types.ContainerLogsOptions logs io.ReadCloser buf *bytes.Buffer err error ) client, err := docker.NewDockerClient() if err != nil { return "", err } options = types.ContainerLogsOptions{ ShowStdout: true, Timestamps: true, } if req.Mode != "all" { options.Since = req.Mode } if logs, err = client.ContainerLogs(context.Background(), req.ContainerID, options); err != nil { return "", err } defer logs.Close() buf = new(bytes.Buffer) if _, err = stdcopy.StdCopy(buf, nil, logs); err != nil { return "", err } return buf.String(), nil } func (u *ContainerService) PageNetwork(req dto.PageInfo) (int64, interface{}, error) { client, err := docker.NewDockerClient() if err != nil { return 0, nil, err } list, err := client.NetworkList(context.TODO(), types.NetworkListOptions{}) if err != nil { return 0, nil, err } var ( data []dto.Network records []types.NetworkResource ) total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize if start > total { records = make([]types.NetworkResource, 0) } else { if end >= total { end = total } records = list[start:end] } for _, item := range records { tag := make([]string, 0) for key, val := range item.Labels { tag = append(tag, fmt.Sprintf("%s=%s", key, val)) } var ipam network.IPAMConfig if len(item.IPAM.Config) > 0 { ipam = item.IPAM.Config[0] } data = append(data, dto.Network{ ID: item.ID, CreatedAt: item.Created, Name: item.Name, Driver: item.Driver, IPAMDriver: item.IPAM.Driver, Subnet: ipam.Subnet, Gateway: ipam.Gateway, Attachable: item.Attachable, Labels: tag, }) } return int64(total), data, nil } func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error { client, err := docker.NewDockerClient() if err != nil { return err } for _, id := range req.Ids { if err := client.NetworkRemove(context.TODO(), id); err != nil { return err } } return nil } func (u *ContainerService) CreateNetwork(req dto.NetworkCreat) error { client, err := docker.NewDockerClient() if err != nil { return err } var ( ipam network.IPAMConfig hasConf bool ) if len(req.Subnet) != 0 { ipam.Subnet = req.Subnet hasConf = true } if len(req.Gateway) != 0 { ipam.Gateway = req.Gateway hasConf = true } if len(req.IPRange) != 0 { ipam.IPRange = req.IPRange hasConf = true } options := types.NetworkCreate{ Driver: req.Driver, Options: stringsToMap(req.Options), Labels: stringsToMap(req.Labels), } if hasConf { options.IPAM = &network.IPAM{Config: []network.IPAMConfig{ipam}} } if _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil { return err } return nil } func (u *ContainerService) PageVolume(req dto.PageInfo) (int64, interface{}, error) { client, err := docker.NewDockerClient() if err != nil { return 0, nil, err } list, err := client.VolumeList(context.TODO(), filters.NewArgs()) if err != nil { return 0, nil, err } var ( data []dto.Volume records []*types.Volume ) total, start, end := len(list.Volumes), (req.Page-1)*req.PageSize, req.Page*req.PageSize if start > total { records = make([]*types.Volume, 0) } else { if end >= total { end = total } records = list.Volumes[start:end] } for _, item := range records { tag := make([]string, 0) for _, val := range item.Labels { tag = append(tag, val) } createTime, _ := time.Parse("2006-01-02T15:04:05Z", item.CreatedAt) data = append(data, dto.Volume{ CreatedAt: createTime, Name: item.Name, Driver: item.Driver, Mountpoint: item.Mountpoint, Labels: tag, }) } return int64(total), data, nil } func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error { client, err := docker.NewDockerClient() if err != nil { return err } for _, id := range req.Ids { if err := client.VolumeRemove(context.TODO(), id, true); err != nil { return err } } return nil } func (u *ContainerService) CreateVolume(req dto.VolumeCreat) error { client, err := docker.NewDockerClient() if err != nil { return err } options := volume.VolumeCreateBody{ Name: req.Name, Driver: req.Driver, DriverOpts: stringsToMap(req.Options), Labels: stringsToMap(req.Labels), } if _, err := client.VolumeCreate(context.TODO(), options); err != nil { return err } return nil } func stringsToMap(list []string) map[string]string { var lableMap = make(map[string]string) for _, label := range list { sps := strings.Split(label, "=") if len(sps) > 1 { lableMap[sps[0]] = sps[1] } } return lableMap }