mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-02-28 11:04:14 +08:00
feat: Added progress display for image pulling (#7955)
This commit is contained in:
parent
b80c55c59c
commit
4087a7af3c
@ -612,6 +612,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dockerCLi, err := docker.NewClient()
|
||||
if req.PullImage {
|
||||
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(detail.DockerCompose))
|
||||
if err != nil {
|
||||
@ -619,10 +620,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
}
|
||||
for _, image := range images {
|
||||
t.Log(i18n.GetWithName("PullImageStart", image))
|
||||
if out, err := cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil {
|
||||
if out != "" {
|
||||
err = errors.New(out)
|
||||
}
|
||||
if err = dockerCLi.PullImageWithProcess(t, image); err != nil {
|
||||
err = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||
return err
|
||||
}
|
||||
@ -1046,6 +1044,10 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
return err
|
||||
}
|
||||
imagePrefix := xpack.GetImagePrefix()
|
||||
dockerCLi, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, image := range images {
|
||||
if imagePrefix != "" {
|
||||
lastSlashIndex := strings.LastIndex(image, "/")
|
||||
@ -1054,17 +1056,19 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
}
|
||||
image = imagePrefix + "/" + image
|
||||
}
|
||||
|
||||
task.Log(i18n.GetWithName("PullImageStart", image))
|
||||
if out, err = cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil {
|
||||
if out != "" {
|
||||
if strings.Contains(out, "no such host") {
|
||||
if err = dockerCLi.PullImageWithProcess(task, image); err != nil {
|
||||
errOur := err.Error()
|
||||
if errOur != "" {
|
||||
if strings.Contains(errOur, "no such host") {
|
||||
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
||||
}
|
||||
if strings.Contains(out, "timeout") {
|
||||
if strings.Contains(errOur, "timeout") {
|
||||
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
|
||||
}
|
||||
}
|
||||
appInstall.Message = errMsg + out
|
||||
appInstall.Message = errMsg + errOur
|
||||
installErr := errors.New(appInstall.Message)
|
||||
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), installErr)
|
||||
return installErr
|
||||
|
@ -1375,7 +1375,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
|
||||
return buserr.New("ErrPHPResource")
|
||||
}
|
||||
website.RuntimeID = req.RuntimeID
|
||||
phpProxy := fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
||||
phpProxy := fmt.Sprintf("127.0.0.1:%s", runtime.Port)
|
||||
website.Proxy = phpProxy
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, "")
|
||||
website.Type = constant.Runtime
|
||||
|
@ -849,7 +849,7 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
}
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
} else {
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", runtime.Port)
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%s", runtime.Port)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
@ -26,6 +27,7 @@ type Task struct {
|
||||
Name string
|
||||
TaskID string
|
||||
Logger *log.Logger
|
||||
Writer *bufio.Writer
|
||||
SubTasks []*SubTask
|
||||
Rollbacks []RollbackFunc
|
||||
logFile *os.File
|
||||
@ -111,6 +113,7 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
writer := bufio.NewWriter(file)
|
||||
logger := log.New(file, "", log.LstdFlags)
|
||||
taskModel := &model.Task{
|
||||
ID: taskID,
|
||||
@ -122,7 +125,7 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
|
||||
Operate: operate,
|
||||
}
|
||||
taskRepo := repo.NewITaskRepo()
|
||||
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel}
|
||||
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel, Writer: writer}
|
||||
return task, nil
|
||||
}
|
||||
|
||||
|
@ -2,17 +2,21 @@ package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
|
||||
"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/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewDockerClient() (*client.Client, error) {
|
||||
@ -28,10 +32,6 @@ func NewDockerClient() (*client.Client, error) {
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
cli *client.Client
|
||||
}
|
||||
|
||||
func NewClient() (Client, error) {
|
||||
var settingItem model.Setting
|
||||
_ = global.DB.Where("key = ?", "DockerSockPath").First(&settingItem).Error
|
||||
@ -48,10 +48,8 @@ func NewClient() (Client, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewClientWithCli(cli *client.Client) (Client, error) {
|
||||
return Client{
|
||||
cli: cli,
|
||||
}, nil
|
||||
type Client struct {
|
||||
cli *client.Client
|
||||
}
|
||||
|
||||
func (c Client) Close() {
|
||||
@ -142,6 +140,7 @@ func CreateDefaultDockerNetwork() error {
|
||||
global.LOG.Errorf("init docker client error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
defer cli.Close()
|
||||
if !cli.NetworkExist("1panel-network") {
|
||||
if err := cli.CreateNetwork("1panel-network"); err != nil {
|
||||
@ -151,3 +150,66 @@ func CreateDefaultDockerNetwork() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setLog(id, newLastLine string, task *task.Task) error {
|
||||
data, err := os.ReadFile(task.Task.LogFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file: %v", err)
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
exist := false
|
||||
for index, line := range lines {
|
||||
if strings.Contains(line, id) {
|
||||
lines[index] = newLastLine
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
task.Log(newLastLine)
|
||||
return nil
|
||||
}
|
||||
output := strings.Join(lines, "\n")
|
||||
_ = os.WriteFile(task.Task.LogFile, []byte(output), os.ModePerm)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Client) PullImageWithProcess(task *task.Task, imageName string) error {
|
||||
out, err := c.cli.ImagePull(context.Background(), imageName, image.PullOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
decoder := json.NewDecoder(out)
|
||||
for {
|
||||
var progress map[string]interface{}
|
||||
if err = decoder.Decode(&progress); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
timeStr := time.Now().Format("2006/01/02 15:04:05")
|
||||
status, _ := progress["status"].(string)
|
||||
if status == "Downloading" || status == "Extracting" {
|
||||
id, _ := progress["id"].(string)
|
||||
progressDetail, _ := progress["progressDetail"].(map[string]interface{})
|
||||
current, _ := progressDetail["current"].(float64)
|
||||
progressStr := ""
|
||||
total, ok := progressDetail["total"].(float64)
|
||||
if ok {
|
||||
progressStr = fmt.Sprintf("%s %s [%s] --- %.2f%%", timeStr, status, id, (current/total)*100)
|
||||
} else {
|
||||
progressStr = fmt.Sprintf("%s %s [%s] --- %.2f%%", timeStr, status, id, current)
|
||||
}
|
||||
|
||||
_ = setLog(id, progressStr, task)
|
||||
}
|
||||
if status == "Pull complete" || status == "Download complete" {
|
||||
id, _ := progress["id"].(string)
|
||||
progressStr := fmt.Sprintf("%s %s [%s] --- %.2f%%", timeStr, status, id, 100.0)
|
||||
_ = setLog(id, progressStr, task)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ var WebUrlMap = map[string]struct{}{
|
||||
"/xpack/waf/websites": {},
|
||||
"/xpack/waf/log": {},
|
||||
"/xpack/waf/block": {},
|
||||
"/xpack/waf/blackwhite": {},
|
||||
"/xpack/monitor/dashboard": {},
|
||||
"/xpack/monitor/setting": {},
|
||||
"/xpack/monitor/rank": {},
|
||||
|
@ -85,7 +85,7 @@ const search = async () => {
|
||||
};
|
||||
|
||||
const openTaskLog = (row: Log.Task) => {
|
||||
taskLogRef.value.openWithTaskID(row.id, !(row.status == 'Executing'));
|
||||
taskLogRef.value.openWithTaskID(row.id, row.status == 'Executing');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex items-center justify-center px-8 py-6">
|
||||
<div class="w-full h-full flex items-center justify-center px-8">
|
||||
<div v-loading="loading" class="w-full flex-grow flex flex-col">
|
||||
<div v-if="mfaShow">
|
||||
<el-form @submit.prevent>
|
||||
|
@ -9,7 +9,7 @@
|
||||
class="bg-white shadow-lg relative z-10 border border-gray-200 flex overflow-hidden"
|
||||
>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 items-stretch w-full h-full">
|
||||
<div v-if="showLogo" class="flex flex-col justify-center items-center w-full">
|
||||
<div v-if="showLogo">
|
||||
<img :src="logoImage" class="max-w-full max-h-full object-contain" />
|
||||
</div>
|
||||
<div :class="loginFormClass">
|
||||
|
Loading…
x
Reference in New Issue
Block a user