feat: 应用升级增加日志 (#6011)
Some checks failed
sync2gitee / repo-sync (push) Failing after -7m21s

This commit is contained in:
zhengkunwang 2024-08-02 16:00:15 +08:00 committed by GitHub
parent bdece10868
commit 25b189069c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 156 additions and 123 deletions

View file

@ -72,6 +72,7 @@ type AppInstalledOperate struct {
Backup bool `json:"backup"` Backup bool `json:"backup"`
PullImage bool `json:"pullImage"` PullImage bool `json:"pullImage"`
DockerCompose string `json:"dockerCompose"` DockerCompose string `json:"dockerCompose"`
TaskID string `json:"taskID"`
} }
type AppInstallUpgrade struct { type AppInstallUpgrade struct {
@ -80,6 +81,7 @@ type AppInstallUpgrade struct {
Backup bool `json:"backup"` Backup bool `json:"backup"`
PullImage bool `json:"pullImage"` PullImage bool `json:"pullImage"`
DockerCompose string `json:"dockerCompose"` DockerCompose string `json:"dockerCompose"`
TaskID string `json:"taskID"`
} }
type AppInstalledUpdate struct { type AppInstalledUpdate struct {

View file

@ -128,8 +128,13 @@ type FileReadByLineReq struct {
ID uint `json:"ID"` ID uint `json:"ID"`
Name string `json:"name"` Name string `json:"name"`
Latest bool `json:"latest"` Latest bool `json:"latest"`
TaskID string `json:"taskID"` TaskReq
TaskType string `json:"taskType"` }
type TaskReq struct {
TaskID string `json:"taskID"`
TaskType string `json:"taskType"`
TaskOperate string `json:"taskOperate"`
} }
type FileExistReq struct { type FileExistReq struct {

View file

@ -6,6 +6,7 @@ type Task struct {
ID string `gorm:"primarykey;" json:"id"` ID string `gorm:"primarykey;" json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
Operate string `json:"operate"`
LogFile string `json:"logFile"` LogFile string `json:"logFile"`
Status string `json:"status"` Status string `json:"status"`
ErrorMsg string `json:"errorMsg"` ErrorMsg string `json:"errorMsg"`

View file

@ -19,6 +19,7 @@ type ITaskRepo interface {
WithType(taskType string) DBOption WithType(taskType string) DBOption
WithResourceID(id uint) DBOption WithResourceID(id uint) DBOption
WithStatus(status string) DBOption WithStatus(status string) DBOption
WithOperate(taskOperate string) DBOption
} }
func NewITaskRepo() ITaskRepo { func NewITaskRepo() ITaskRepo {
@ -37,6 +38,12 @@ func (t TaskRepo) WithType(taskType string) DBOption {
} }
} }
func (t TaskRepo) WithOperate(taskOperate string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("operate = ?", taskOperate)
}
}
func (t TaskRepo) WithStatus(status string) DBOption { func (t TaskRepo) WithStatus(status string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("status = ?", status) return g.Where("status = ?", status)

View file

@ -185,7 +185,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version) versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)
if !fileOp.Stat(versionPath) || detail.Update { if !fileOp.Stat(versionPath) || detail.Update {
if err = downloadApp(app, detail, nil); err != nil { if err = downloadApp(app, detail, nil, nil); err != nil {
return appDetailDTO, err return appDetailDTO, err
} }
} }
@ -441,7 +441,7 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
return return
} }
installTask, err := task.NewTaskWithOps(appInstall.Name, task.TaskCreate, task.TaskScopeApp, req.TaskID, appInstall.ID) installTask, err := task.NewTaskWithOps(appInstall.Name, task.TaskInstall, task.TaskScopeApp, req.TaskID, appInstall.ID)
if err != nil { if err != nil {
return return
} }
@ -462,7 +462,7 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
return nil return nil
} }
handleAppStatus := func() { handleAppStatus := func(t *task.Task) {
appInstall.Status = constant.UpErr appInstall.Status = constant.UpErr
appInstall.Message = installTask.Task.ErrorMsg appInstall.Message = installTask.Task.ErrorMsg
_ = appInstallRepo.Save(context.Background(), appInstall) _ = appInstallRepo.Save(context.Background(), appInstall)

View file

@ -272,6 +272,7 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
Backup: req.Backup, Backup: req.Backup,
PullImage: req.PullImage, PullImage: req.PullImage,
DockerCompose: req.DockerCompose, DockerCompose: req.DockerCompose,
TaskID: req.TaskID,
} }
return upgradeInstall(upgradeReq) return upgradeInstall(upgradeReq)
case constant.Reload: case constant.Reload:

View file

@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/agent/app/task" "github.com/1Panel-dev/1Panel/agent/app/task"
"log"
"math" "math"
"net/http" "net/http"
"os" "os"
@ -132,7 +133,7 @@ var ToolKeys = map[string]uint{
} }
func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error { func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
deleteAppLink := func() { deleteAppLink := func(t *task.Task) {
_ = deleteLink(ctx, appInstall, true, true, true) _ = deleteLink(ctx, appInstall, true, true, true)
} }
var dbConfig dto.AppDatabase var dbConfig dto.AppDatabase
@ -496,67 +497,44 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
if install.Version == detail.Version { if install.Version == detail.Version {
return errors.New("two version is same") return errors.New("two version is same")
} }
upgradeTask, err := task.NewTaskWithOps(install.Name, task.TaskUpgrade, task.TaskScopeApp, req.TaskID, install.ID)
if err != nil {
return err
}
install.Status = constant.Upgrading install.Status = constant.Upgrading
go func() { var (
var ( upErr error
upErr error backupFile string
backupFile string )
preErr error backUpApp := func(t *task.Task) error {
)
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppStart", install.Name, nil))
if req.Backup { if req.Backup {
backupRecord, err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}) backupRecord, err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name})
if err == nil { if err != nil {
localDir, err := loadLocalDir() return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
if err == nil {
backupFile = path.Join(localDir, backupRecord.FileDir, backupRecord.FileName)
} else {
global.LOG.Errorf(i18n.GetMsgWithName("ErrAppBackup", install.Name, err))
}
} else {
global.LOG.Errorf(i18n.GetMsgWithName("ErrAppBackup", install.Name, err))
} }
localDir, err := loadLocalDir()
if err != nil {
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
}
backupFile = path.Join(localDir, backupRecord.FileDir, backupRecord.FileName)
} }
return nil
}
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backUpApp, nil)
defer func() { upgradeApp := func(t *task.Task) error {
if upErr != nil {
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, upErr))
if req.Backup {
global.LOG.Infof(i18n.GetMsgWithName("AppRecover", install.Name, nil))
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal, File: backupFile}); err != nil {
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
}
}
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
if existInstall.ID > 0 {
existInstall.Status = constant.UpgradeErr
existInstall.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &existInstall)
}
}
if preErr != nil {
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, preErr))
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
if existInstall.ID > 0 {
existInstall.Status = constant.UpgradeErr
existInstall.Message = preErr.Error()
_ = appInstallRepo.Save(context.Background(), &existInstall)
}
}
}()
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version) detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
if install.App.Resource == constant.AppResourceRemote { if install.App.Resource == constant.AppResourceRemote {
if preErr = downloadApp(install.App, detail, &install); preErr != nil { if err = downloadApp(install.App, detail, &install, t.Logger); err != nil {
return return err
} }
if detail.DockerCompose == "" { if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml")) composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil { if err != nil {
preErr = err return err
return
} }
detail.DockerCompose = string(composeDetail) detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail) _ = appDetailRepo.Update(context.Background(), detail)
@ -565,41 +543,37 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
_, _, _ = httpUtil.HandleGet(detail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s) _, _, _ = httpUtil.HandleGet(detail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s)
}() }()
} }
if install.App.Resource == constant.AppResourceLocal { if install.App.Resource == constant.AppResourceLocal {
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version) detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
} }
content, err := fileOp.GetContent(install.GetEnvPath()) content, err := fileOp.GetContent(install.GetEnvPath())
if err != nil { if err != nil {
preErr = err return err
return
} }
if req.PullImage { if req.PullImage {
projectName := strings.ToLower(install.Name) projectName := strings.ToLower(install.Name)
images, err := composeV2.GetDockerComposeImages(projectName, content, []byte(detail.DockerCompose)) images, err := composeV2.GetDockerComposeImages(projectName, content, []byte(detail.DockerCompose))
if err != nil { if err != nil {
preErr = err return err
return
} }
for _, image := range images { for _, image := range images {
global.LOG.Infof(i18n.GetMsgWithName("PullImageStart", image, nil)) t.Log(i18n.GetWithName("PullImageStart", image))
if out, err := cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil { if out, err := cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil {
if out != "" { if out != "" {
err = errors.New(out) err = errors.New(out)
} }
preErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err) err = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return return err
} else {
global.LOG.Infof(i18n.GetMsgByKey("PullImageSuccess"))
} }
t.LogSuccess(i18n.GetMsgByKey("PullImage"))
} }
} }
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath())) command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
stdout, _ := command.CombinedOutput() stdout, _ := command.CombinedOutput()
if stdout != nil { if stdout != nil {
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout)) t.Logger.Printf("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
} }
sourceScripts := path.Join(detailDir, "scripts") sourceScripts := path.Join(detailDir, "scripts")
if fileOp.Stat(sourceScripts) { if fileOp.Stat(sourceScripts) {
@ -612,9 +586,9 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
var newCompose string var newCompose string
if req.DockerCompose == "" { if req.DockerCompose == "" {
newCompose, upErr = getUpgradeCompose(install, detail) newCompose, err = getUpgradeCompose(install, detail)
if upErr != nil { if err != nil {
return return err
} }
} else { } else {
newCompose = req.DockerCompose newCompose = req.DockerCompose
@ -627,40 +601,65 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
if out, err := compose.Down(install.GetComposePath()); err != nil { if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" { if out != "" {
upErr = errors.New(out) upErr = errors.New(out)
return return upErr
} }
upErr = err return err
return
} }
envs := make(map[string]interface{}) envs := make(map[string]interface{})
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil { if err = json.Unmarshal([]byte(install.Env), &envs); err != nil {
return return err
} }
envParams := make(map[string]string, len(envs)) envParams := make(map[string]string, len(envs))
handleMap(envs, envParams) handleMap(envs, envParams)
if upErr = env.Write(envParams, install.GetEnvPath()); upErr != nil { if err = env.Write(envParams, install.GetEnvPath()); err != nil {
return return err
} }
//TODO use task if err = runScript(t, &install, "upgrade"); err != nil {
if upErr = runScript(nil, &install, "upgrade"); upErr != nil { return err
return
} }
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil { if err = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
return return err
} }
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("Run"), i18n.GetMsgByKey("App"))
t.Log(logStr)
if out, err := compose.Up(install.GetComposePath()); err != nil { if out, err := compose.Up(install.GetComposePath()); err != nil {
if out != "" { if out != "" {
upErr = errors.New(out) return errors.New(out)
}
return err
}
t.LogSuccess(logStr)
install.Status = constant.Running
return appInstallRepo.Save(context.Background(), &install)
}
rollBackApp := func(t *task.Task) {
if req.Backup {
t.Log(i18n.GetWithName("AppRecover", install.Name))
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal, File: backupFile}); err != nil {
t.LogFailedWithErr(i18n.GetWithName("AppRecover", install.Name), err)
return return
} }
upErr = err t.LogSuccess(i18n.GetWithName("AppRecover", install.Name))
return return
} }
install.Status = constant.Running }
_ = appInstallRepo.Save(context.Background(), &install)
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppSuccess", install.Name, nil)) upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp)
go func() {
err = upgradeTask.Execute()
if err != nil {
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
if existInstall.ID > 0 && existInstall.Status != constant.Running {
existInstall.Status = constant.UpgradeErr
existInstall.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &existInstall)
}
}
}() }()
return appInstallRepo.Save(context.Background(), &install) return appInstallRepo.Save(context.Background(), &install)
@ -747,9 +746,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
} }
} }
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) { func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *log.Logger) (err error) {
if app.IsLocalApp() { if app.IsLocalApp() {
//本地应用,不去官网下载
return nil return nil
} }
appResourceDir := path.Join(constant.AppResourceDir, app.Resource) appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
@ -765,7 +763,12 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
if !fileOp.Stat(appVersionDir) { if !fileOp.Stat(appVersionDir) {
_ = fileOp.CreateDir(appVersionDir, 0755) _ = fileOp.CreateDir(appVersionDir, 0755)
} }
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl) if logger == nil {
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
} else {
logger.Printf("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
}
filePath := path.Join(appVersionDir, app.Key+"-"+appDetail.Version+".tar.gz") filePath := path.Join(appVersionDir, app.Key+"-"+appDetail.Version+".tar.gz")
defer func() { defer func() {
@ -778,11 +781,19 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
}() }()
if err = fileOp.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil { if err = fileOp.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {
global.LOG.Errorf("download app[%s] error %v", app.Name, err) if logger == nil {
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
} else {
logger.Printf("download app[%s] error %v", app.Name, err)
}
return return
} }
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil { if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err) if logger == nil {
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
} else {
logger.Printf("decompress app[%s] error %v", app.Name, err)
}
return return
} }
_ = fileOp.DeleteFile(filePath) _ = fileOp.DeleteFile(filePath)
@ -796,7 +807,7 @@ func copyData(task *task.Task, app model.App, appDetail model.AppDetail, appInst
appResourceDir := path.Join(constant.AppResourceDir, app.Resource) appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
if app.Resource == constant.AppResourceRemote { if app.Resource == constant.AppResourceRemote {
err = downloadApp(app, appDetail, appInstall) err = downloadApp(app, appDetail, appInstall, task.Logger)
if err != nil { if err != nil {
return return
} }

View file

@ -472,7 +472,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
if req.TaskID != "" { if req.TaskID != "" {
opts = append(opts, taskRepo.WithByID(req.TaskID)) opts = append(opts, taskRepo.WithByID(req.TaskID))
} else { } else {
opts = append(opts, taskRepo.WithType(req.TaskType), taskRepo.WithResourceID(req.ID)) opts = append(opts, taskRepo.WithType(req.TaskType), taskRepo.WithOperate(req.TaskOperate), taskRepo.WithResourceID(req.ID))
} }
taskModel, err := taskRepo.GetFirst(opts...) taskModel, err := taskRepo.GetFirst(opts...)
if err != nil { if err != nil {

View file

@ -114,7 +114,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
appVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version) appVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version)
if !fileOp.Stat(appVersionDir) || appDetail.Update { if !fileOp.Stat(appVersionDir) || appDetail.Update {
if err = downloadApp(app, appDetail, nil); err != nil { if err = downloadApp(app, appDetail, nil, nil); err != nil {
return nil, err return nil, err
} }
} }
@ -397,7 +397,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version) appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
if !fileOp.Stat(appVersionDir) || appDetail.Update { if !fileOp.Stat(appVersionDir) || appDetail.Update {
if err := downloadApp(app, appDetail, nil); err != nil { if err := downloadApp(app, appDetail, nil, nil); err != nil {
return err return err
} }
_ = fileOp.Rename(path.Join(runtime.GetPath(), "run.sh"), path.Join(runtime.GetPath(), "run.sh.bak")) _ = fileOp.Rename(path.Join(runtime.GetPath(), "run.sh"), path.Join(runtime.GetPath(), "run.sh.bak"))

View file

@ -347,7 +347,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
website.FtpID = itemID website.FtpID = itemID
return nil return nil
} }
deleteFtpUser := func() { deleteFtpUser := func(t *task.Task) {
if website.FtpID > 0 { if website.FtpID > 0 {
req := dto.BatchDeleteReq{Ids: []uint{website.FtpID}} req := dto.BatchDeleteReq{Ids: []uint{website.FtpID}}
if err = NewIFtpService().Delete(req); err != nil { if err = NewIFtpService().Delete(req); err != nil {
@ -381,7 +381,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
return nil return nil
} }
deleteWebsite := func() { deleteWebsite := func(t *task.Task) {
_ = deleteWebsiteFolder(nginxInstall, website) _ = deleteWebsiteFolder(nginxInstall, website)
} }

View file

@ -16,7 +16,7 @@ import (
) )
type ActionFunc func(*Task) error type ActionFunc func(*Task) error
type RollbackFunc func() type RollbackFunc func(*Task)
type Task struct { type Task struct {
Name string Name string
@ -48,6 +48,7 @@ const (
TaskUpgrade = "TaskUpgrade" TaskUpgrade = "TaskUpgrade"
TaskUpdate = "TaskUpdate" TaskUpdate = "TaskUpdate"
TaskRestart = "TaskRestart" TaskRestart = "TaskRestart"
TaskBackup = "TaskBackup"
) )
const ( const (
@ -67,29 +68,20 @@ func GetTaskName(resourceName, operate, scope string) string {
} }
func NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) { func NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {
return NewTask(GetTaskName(resourceName, operate, scope), scope, taskID, resourceID) return NewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)
} }
//func NewChildTask(name, taskType, parentTaskID string) (*Task, error) { func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {
// task, err := NewTask(name, taskType, "")
// if err != nil {
// return nil, err
// }
// task.ParentID = parentTaskID
// return task, nil
//}
func NewTask(name, taskType, taskID string, resourceID uint) (*Task, error) {
if taskID == "" { if taskID == "" {
taskID = uuid.New().String() taskID = uuid.New().String()
} }
logDir := path.Join(constant.LogDir, taskType) logDir := path.Join(constant.LogDir, taskScope)
if _, err := os.Stat(logDir); os.IsNotExist(err) { if _, err := os.Stat(logDir); os.IsNotExist(err) {
if err = os.MkdirAll(logDir, 0755); err != nil { if err = os.MkdirAll(logDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err) return nil, fmt.Errorf("failed to create log directory: %w", err)
} }
} }
logPath := path.Join(constant.LogDir, taskType, taskID+".log") logPath := path.Join(constant.LogDir, taskScope, taskID+".log")
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666) file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err) return nil, fmt.Errorf("failed to open log file: %w", err)
@ -98,10 +90,11 @@ func NewTask(name, taskType, taskID string, resourceID uint) (*Task, error) {
taskModel := &model.Task{ taskModel := &model.Task{
ID: taskID, ID: taskID,
Name: name, Name: name,
Type: taskType, Type: taskScope,
LogFile: logPath, LogFile: logPath,
Status: constant.StatusRunning, Status: constant.StatusRunning,
ResourceID: resourceID, ResourceID: resourceID,
Operate: operate,
} }
taskRepo := repo.NewITaskRepo() 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}
@ -147,7 +140,7 @@ func (s *SubTask) Execute() error {
if i == s.Retry { if i == s.Retry {
if s.Rollback != nil { if s.Rollback != nil {
s.Rollback() s.Rollback(s.RootTask)
} }
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
@ -176,7 +169,7 @@ func (t *Task) Execute() error {
t.Task.ErrorMsg = err.Error() t.Task.ErrorMsg = err.Error()
t.Task.Status = constant.StatusFailed t.Task.Status = constant.StatusFailed
for _, rollback := range t.Rollbacks { for _, rollback := range t.Rollbacks {
rollback() rollback(t)
} }
t.updateTask(t.Task) t.updateTask(t.Task)
break break

View file

@ -201,11 +201,11 @@ ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证" ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
#task #task
TaskStart: "{{.name}} 开始 [START]" TaskStart: "{{.name}} 任务开始 [START]"
TaskEnd: "{{.name}} 结束 [COMPLETED]" TaskEnd: "{{.name}} 任务结束 [COMPLETED]"
TaskFailed: "{{.name}} 失败" TaskFailed: "{{.name}} 任务失败"
TaskTimeout: "{{.name}} 超时" TaskTimeout: "{{.name}} 超时"
TaskSuccess: "{{.name}} 成功" TaskSuccess: "{{.name}} 任务成功"
TaskRetry: "开始第 {{.name}} 次重试" TaskRetry: "开始第 {{.name}} 次重试"
SubTaskSuccess: "{{ .name }} 成功" SubTaskSuccess: "{{ .name }} 成功"
SubTaskFailed: "{{ .name }} 失败: {{ .err }}" SubTaskFailed: "{{ .name }} 失败: {{ .err }}"
@ -216,6 +216,7 @@ TaskDelete: "删除"
TaskUpgrade: "升级" TaskUpgrade: "升级"
TaskUpdate: "更新" TaskUpdate: "更新"
TaskRestart: "重启" TaskRestart: "重启"
TaskBackup: "备份"
Website: "网站" Website: "网站"
App: "应用" App: "应用"
Runtime: "运行环境" Runtime: "运行环境"
@ -232,4 +233,4 @@ HandleDatabaseApp: "处理应用参数"
ExecShell: "执行 {{ .name }} 脚本" ExecShell: "执行 {{ .name }} 脚本"
PullImage: "拉取镜像" PullImage: "拉取镜像"
Start: "开始" Start: "开始"
Run: "启动" Run: "启动"

View file

@ -276,7 +276,7 @@ var InitPHPExtensions = &gormigrate.Migration{
} }
var AddTask = &gormigrate.Migration{ var AddTask = &gormigrate.Migration{
ID: "20240801-add-task", ID: "20240802-add-task",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate( return tx.AutoMigrate(
&model.Task{}) &model.Task{})

View file

@ -53,6 +53,7 @@ const readReq = reactive({
pageSize: 500, pageSize: 500,
latest: false, latest: false,
taskType: '', taskType: '',
taskOperate: '',
id: 0, id: 0,
}); });
@ -69,9 +70,10 @@ const openWithTaskID = (id: string) => {
initData(); initData();
}; };
const openWithResourceID = (taskType: string, resourceID: number) => { const openWithResourceID = (taskType: string, taskOperate: string, resourceID: number) => {
readReq.taskType = taskType; readReq.taskType = taskType;
readReq.id = resourceID; readReq.id = resourceID;
readReq.taskOperate = taskOperate;
initData(); initData();
}; };

View file

@ -270,9 +270,7 @@ const openTaskLog = (taskID: string) => {
const install = () => { const install = () => {
loading.value = true; loading.value = true;
const taskID = uuidv4(); const taskID = uuidv4();
console.log(taskID);
req.taskID = taskID; req.taskID = taskID;
console.log(req);
InstallApp(req) InstallApp(req)
.then(() => { .then(() => {
handleClose(); handleClose();

View file

@ -116,7 +116,6 @@
<el-button link type="info"> <el-button link type="info">
<span class="name">{{ installed.name }}</span> <span class="name">{{ installed.name }}</span>
</el-button> </el-button>
<span class="status"> <span class="status">
<Status <Status
:key="installed.status" :key="installed.status"
@ -161,6 +160,7 @@
</span> </span>
<span class="ml-1"> <span class="ml-1">
<el-tooltip <el-tooltip
v-if="mode !== 'upgrade'"
effect="dark" effect="dark"
:content="$t('commons.button.log')" :content="$t('commons.button.log')"
placement="top" placement="top"
@ -621,7 +621,7 @@ const quickJump = () => {
const openLog = (row: any) => { const openLog = (row: any) => {
switch (row.status) { switch (row.status) {
case 'Installing': case 'Installing':
taskLogRef.value.openWithResourceID('App', row.id); taskLogRef.value.openWithResourceID('App', 'TaskInstall', row.id);
break; break;
default: default:
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name }); composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });

View file

@ -76,6 +76,7 @@
</template> </template>
<Diff ref="composeDiffRef" @confirm="getNewCompose" /> <Diff ref="composeDiffRef" @confirm="getNewCompose" />
</DrawerPro> </DrawerPro>
<TaskLog ref="taskLogRef" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { App } from '@/api/interface/app'; import { App } from '@/api/interface/app';
@ -88,6 +89,8 @@ import { Rules } from '@/global/form-rules';
import Diff from './diff/index.vue'; import Diff from './diff/index.vue';
import bus from '../../bus'; import bus from '../../bus';
import CodemirrorPro from '@/components/codemirror-pro/index.vue'; import CodemirrorPro from '@/components/codemirror-pro/index.vue';
import TaskLog from '@/components/task-log/index.vue';
import { v4 as uuidv4 } from 'uuid';
const composeDiffRef = ref(); const composeDiffRef = ref();
const updateRef = ref<FormInstance>(); const updateRef = ref<FormInstance>();
@ -102,6 +105,7 @@ const operateReq = reactive({
pullImage: true, pullImage: true,
version: '', version: '',
dockerCompose: '', dockerCompose: '',
taskID: '',
}); });
const resourceName = ref(''); const resourceName = ref('');
const rules = ref<any>({ const rules = ref<any>({
@ -119,6 +123,7 @@ const handleClose = () => {
const newCompose = ref(''); const newCompose = ref('');
const useNewCompose = ref(false); const useNewCompose = ref(false);
const appInstallID = ref(0); const appInstallID = ref(0);
const taskLogRef = ref();
const toLink = (link: string) => { const toLink = (link: string) => {
window.open(link, '_blank'); window.open(link, '_blank');
@ -185,17 +190,24 @@ const getVersions = async (version: string) => {
} catch (error) {} } catch (error) {}
}; };
const openTaskLog = (taskID: string) => {
taskLogRef.value.openWithTaskID(taskID);
};
const operate = async () => { const operate = async () => {
loading.value = true; loading.value = true;
if (operateReq.operate === 'upgrade') { if (operateReq.operate === 'upgrade') {
if (useNewCompose.value) { if (useNewCompose.value) {
operateReq.dockerCompose = newCompose.value; operateReq.dockerCompose = newCompose.value;
} }
const taskID = uuidv4();
operateReq.taskID = taskID;
await InstalledOp(operateReq) await InstalledOp(operateReq)
.then(() => { .then(() => {
MsgSuccess(i18n.global.t('app.upgradeStart')); MsgSuccess(i18n.global.t('app.upgradeStart'));
bus.emit('upgrade', true); bus.emit('upgrade', true);
handleClose(); handleClose();
openTaskLog(taskID);
}) })
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;