2022-10-10 15:14:49 +08:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
2022-10-12 13:42:58 +08:00
|
|
|
"bufio"
|
2022-10-10 15:14:49 +08:00
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
2022-10-18 18:39:45 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/global"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
2022-10-10 15:14:49 +08:00
|
|
|
"github.com/docker/docker/api/types"
|
2022-10-12 13:42:58 +08:00
|
|
|
"github.com/docker/docker/pkg/archive"
|
2022-10-10 15:14:49 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type ImageService struct{}
|
|
|
|
|
|
|
|
type IImageService interface {
|
|
|
|
Page(req dto.PageInfo) (int64, interface{}, error)
|
2022-10-12 18:55:47 +08:00
|
|
|
List() ([]dto.Options, error)
|
2022-10-10 15:14:49 +08:00
|
|
|
ImagePull(req dto.ImagePull) error
|
|
|
|
ImageLoad(req dto.ImageLoad) error
|
|
|
|
ImageSave(req dto.ImageSave) error
|
|
|
|
ImagePush(req dto.ImagePush) error
|
2022-10-11 17:46:52 +08:00
|
|
|
ImageRemove(req dto.BatchDelete) error
|
2022-10-10 15:14:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewIImageService() IImageService {
|
|
|
|
return &ImageService{}
|
|
|
|
}
|
|
|
|
func (u *ImageService) Page(req dto.PageInfo) (int64, interface{}, error) {
|
|
|
|
var (
|
|
|
|
list []types.ImageSummary
|
|
|
|
records []dto.ImageInfo
|
|
|
|
backDatas []dto.ImageInfo
|
|
|
|
)
|
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return 0, nil, err
|
|
|
|
}
|
2022-10-12 18:55:47 +08:00
|
|
|
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
|
2022-10-10 15:14:49 +08:00
|
|
|
if err != nil {
|
|
|
|
return 0, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, image := range list {
|
|
|
|
size := formatFileSize(image.Size)
|
2022-10-11 17:46:52 +08:00
|
|
|
records = append(records, dto.ImageInfo{
|
|
|
|
ID: image.ID,
|
|
|
|
Tags: image.RepoTags,
|
|
|
|
CreatedAt: time.Unix(image.Created, 0),
|
|
|
|
Size: size,
|
|
|
|
})
|
2022-10-10 15:14:49 +08:00
|
|
|
}
|
|
|
|
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
|
|
|
if start > total {
|
|
|
|
backDatas = make([]dto.ImageInfo, 0)
|
|
|
|
} else {
|
|
|
|
if end >= total {
|
|
|
|
end = total
|
|
|
|
}
|
|
|
|
backDatas = records[start:end]
|
|
|
|
}
|
|
|
|
|
|
|
|
return int64(total), backDatas, nil
|
|
|
|
}
|
|
|
|
|
2022-10-12 18:55:47 +08:00
|
|
|
func (u *ImageService) List() ([]dto.Options, error) {
|
|
|
|
var (
|
|
|
|
list []types.ImageSummary
|
|
|
|
backDatas []dto.Options
|
|
|
|
)
|
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, image := range list {
|
|
|
|
for _, tag := range image.RepoTags {
|
|
|
|
backDatas = append(backDatas, dto.Options{
|
|
|
|
Option: tag,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return backDatas, nil
|
|
|
|
}
|
|
|
|
|
2022-10-12 13:42:58 +08:00
|
|
|
func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if req.From == "edit" {
|
|
|
|
dir := fmt.Sprintf("%s/%s", constant.TmpDockerBuildDir, req.Name)
|
|
|
|
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
|
|
|
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
2022-10-11 14:20:51 +08:00
|
|
|
|
2022-10-12 13:42:58 +08:00
|
|
|
path := fmt.Sprintf("%s/Dockerfile", dir)
|
|
|
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
write := bufio.NewWriter(file)
|
|
|
|
_, _ = write.WriteString(string(req.Dockerfile))
|
|
|
|
write.Flush()
|
|
|
|
req.Dockerfile = dir
|
|
|
|
}
|
|
|
|
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := types.ImageBuildOptions{
|
|
|
|
Dockerfile: "Dockerfile",
|
|
|
|
Tags: []string{req.Name},
|
|
|
|
Remove: true,
|
|
|
|
Labels: stringsToMap(req.Tags),
|
|
|
|
}
|
|
|
|
logName := fmt.Sprintf("%s/build.log", req.Dockerfile)
|
|
|
|
|
|
|
|
path := logName
|
|
|
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
defer file.Close()
|
|
|
|
res, err := client.ImageBuild(context.TODO(), tar, opts)
|
|
|
|
if err != nil {
|
|
|
|
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
global.LOG.Debugf("build image %s successful!", req.Name)
|
|
|
|
_, _ = io.Copy(file, res.Body)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return logName, nil
|
2022-10-11 14:20:51 +08:00
|
|
|
}
|
|
|
|
|
2022-10-10 15:14:49 +08:00
|
|
|
func (u *ImageService) ImagePull(req dto.ImagePull) error {
|
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-10-10 16:47:05 +08:00
|
|
|
if req.RepoID == 0 {
|
|
|
|
go func() {
|
|
|
|
out, err := client.ImagePull(context.TODO(), req.ImageName, types.ImagePullOptions{})
|
|
|
|
if err != nil {
|
|
|
|
global.LOG.Errorf("image %s pull failed, err: %v", req.ImageName, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
_, _ = buf.ReadFrom(out)
|
|
|
|
global.LOG.Debugf("image %s pull stdout: %v", req.ImageName, buf.String())
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
2022-10-10 15:14:49 +08:00
|
|
|
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
options := types.ImagePullOptions{}
|
|
|
|
if repo.Auth {
|
|
|
|
authConfig := types.AuthConfig{
|
|
|
|
Username: repo.Username,
|
|
|
|
Password: repo.Password,
|
|
|
|
}
|
|
|
|
encodedJSON, err := json.Marshal(authConfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
|
|
|
options.RegistryAuth = authStr
|
|
|
|
}
|
|
|
|
image := repo.DownloadUrl + "/" + req.ImageName
|
|
|
|
go func() {
|
2022-10-10 16:47:05 +08:00
|
|
|
out, err := client.ImagePull(context.TODO(), image, options)
|
2022-10-10 15:14:49 +08:00
|
|
|
if err != nil {
|
|
|
|
global.LOG.Errorf("image %s pull failed, err: %v", image, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
_, _ = buf.ReadFrom(out)
|
|
|
|
global.LOG.Debugf("image %s pull stdout: %v", image, buf.String())
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
|
|
|
file, err := os.Open(req.Path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := client.ImageLoad(context.TODO(), file, true); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *ImageService) ImageSave(req dto.ImageSave) error {
|
2022-10-11 17:46:52 +08:00
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer out.Close()
|
2022-10-10 15:14:49 +08:00
|
|
|
file, err := os.OpenFile(fmt.Sprintf("%s/%s.tar", req.Path, req.Name), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
2022-10-11 17:46:52 +08:00
|
|
|
if _, err = io.Copy(file, out); err != nil {
|
2022-10-10 15:14:49 +08:00
|
|
|
return err
|
|
|
|
}
|
2022-10-11 17:46:52 +08:00
|
|
|
return nil
|
|
|
|
}
|
2022-10-10 15:14:49 +08:00
|
|
|
|
2022-10-11 17:46:52 +08:00
|
|
|
func (u *ImageService) ImageTag(req dto.ImageTag) error {
|
|
|
|
client, err := docker.NewDockerClient()
|
2022-10-10 15:14:49 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-10-11 17:46:52 +08:00
|
|
|
|
|
|
|
if err := client.ImageTag(context.TODO(), req.SourceID, req.TargetName); err != nil {
|
2022-10-10 15:14:49 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
options := types.ImagePushOptions{}
|
|
|
|
if repo.Auth {
|
|
|
|
authConfig := types.AuthConfig{
|
|
|
|
Username: repo.Username,
|
|
|
|
Password: repo.Password,
|
|
|
|
}
|
|
|
|
encodedJSON, err := json.Marshal(authConfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
|
|
|
options.RegistryAuth = authStr
|
|
|
|
}
|
2022-10-11 17:46:52 +08:00
|
|
|
newName := fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
|
|
|
|
if newName != req.TagName {
|
|
|
|
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
|
2022-10-11 14:20:51 +08:00
|
|
|
return err
|
|
|
|
}
|
2022-10-10 15:14:49 +08:00
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
out, err := client.ImagePush(context.TODO(), newName, options)
|
|
|
|
if err != nil {
|
2022-10-11 17:46:52 +08:00
|
|
|
global.LOG.Errorf("image %s push failed, err: %v", req.TagName, err)
|
2022-10-10 15:14:49 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
_, _ = buf.ReadFrom(out)
|
2022-10-11 17:46:52 +08:00
|
|
|
global.LOG.Debugf("image %s push stdout: %v", req.TagName, buf.String())
|
2022-10-10 15:14:49 +08:00
|
|
|
}()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-11 17:46:52 +08:00
|
|
|
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
2022-10-10 15:14:49 +08:00
|
|
|
client, err := docker.NewDockerClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-10-11 17:46:52 +08:00
|
|
|
for _, ids := range req.Ids {
|
2022-10-12 13:42:58 +08:00
|
|
|
if _, err := client.ImageRemove(context.TODO(), ids, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil {
|
2022-10-11 17:46:52 +08:00
|
|
|
return err
|
|
|
|
}
|
2022-10-10 15:14:49 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatFileSize(fileSize int64) (size string) {
|
|
|
|
if fileSize < 1024 {
|
|
|
|
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
|
|
|
|
} else if fileSize < (1024 * 1024) {
|
|
|
|
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
|
|
|
|
} else if fileSize < (1024 * 1024 * 1024) {
|
|
|
|
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
|
|
|
|
} else if fileSize < (1024 * 1024 * 1024 * 1024) {
|
|
|
|
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
|
|
|
|
} else if fileSize < (1024 * 1024 * 1024 * 1024 * 1024) {
|
|
|
|
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
|
|
|
|
}
|
|
|
|
}
|