mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-10 23:47:39 +08:00
feat: 应用升级增加日志 (#6011)
Some checks failed
sync2gitee / repo-sync (push) Failing after -7m21s
Some checks failed
sync2gitee / repo-sync (push) Failing after -7m21s
This commit is contained in:
parent
bdece10868
commit
25b189069c
17 changed files with 156 additions and 123 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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"`
|
||||||
|
TaskReq
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskReq struct {
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
TaskType string `json:"taskType"`
|
TaskType string `json:"taskType"`
|
||||||
|
TaskOperate string `json:"taskOperate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileExistReq struct {
|
type FileExistReq struct {
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
|
||||||
)
|
)
|
||||||
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppStart", install.Name, nil))
|
backUpApp := func(t *task.Task) error {
|
||||||
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 {
|
||||||
|
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
|
||||||
|
}
|
||||||
localDir, err := loadLocalDir()
|
localDir, err := loadLocalDir()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
|
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
|
||||||
|
}
|
||||||
backupFile = path.Join(localDir, backupRecord.FileDir, backupRecord.FileName)
|
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))
|
|
||||||
}
|
}
|
||||||
|
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
|
|
||||||
}
|
}
|
||||||
upErr = err
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
t.LogSuccess(logStr)
|
||||||
install.Status = constant.Running
|
install.Status = constant.Running
|
||||||
_ = appInstallRepo.Save(context.Background(), &install)
|
return appInstallRepo.Save(context.Background(), &install)
|
||||||
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppSuccess", install.Name, nil))
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
t.LogSuccess(i18n.GetWithName("AppRecover", install.Name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
if logger == nil {
|
||||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
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 {
|
||||||
|
if logger == nil {
|
||||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
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 {
|
||||||
|
if logger == nil {
|
||||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: "运行环境"
|
||||||
|
|
|
@ -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{})
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue