1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-13 17:24:44 +08:00

feat(system-upgrade): Added support for multi-host upgrade and rollback (#7178)

* feat(system-upgrade): Added support for multi-host upgrade

* feat: Add supports of task display for node upgrade
This commit is contained in:
ssongliu 2024-11-26 14:34:47 +08:00 committed by GitHub
parent df3f105cf0
commit 45362a9819
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 884 additions and 159 deletions

4
.gitignore vendored
View File

@ -4,8 +4,8 @@
*.dll
*.so
*.dylib
build/1panel_agent
build/1panel_core
build/1panel-agent
build/1panel-core
# Mac
.DS_Store

View File

@ -10,11 +10,11 @@ WEB_PATH=$(BASE_PAH)/frontend
ASSERT_PATH= $(BASE_PAH)/core/cmd/server/web/assets
CORE_MAIN= $(BASE_PAH)/cmd/server/main.go
CORE_NAME=1panel_core
CORE_NAME=1panel-core
AGENT_PATH=$(BASE_PAH)/agent
AGENT_MAIN= $(AGENT_PATH)/cmd/server/main.go
AGENT_NAME=1panel_agent
AGENT_NAME=1panel-agent
clean_assets:
@ -61,4 +61,4 @@ build_xpack_all: build_frontend build_core_xpack_on_linux build_agent_xpack_on_l
build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin upx_bin
build_xpack_on_local: clean_assets build_frontend build_agent_xpack_on_darwin build_agent_xpack_on_darwin upx_bin
build_xpack_on_local: clean_assets build_frontend build_core_xpack_on_darwin build_agent_xpack_on_darwin upx_bin

View File

@ -148,7 +148,7 @@ func (u *SnapshotService) HandleSnapshot(req dto.SnapshotCreate) error {
taskItem.AddSubTask(
"SnapCloseDBConn",
func(t *task.Task) error {
taskItem.Log("######################## 6 / 8 ########################")
taskItem.Log("---------------------- 6 / 8 ----------------------")
closeDatabase(itemHelper.snapAgentDB)
closeDatabase(itemHelper.snapCoreDB)
return nil
@ -194,7 +194,7 @@ type snapHelper struct {
}
func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) error {
snap.Task.Log("######################## 1 / 8 ########################")
snap.Task.Log("---------------------- 1 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapDBInfo"))
pathDB := path.Join(global.CONF.System.BaseDir, "1panel/db")
@ -246,17 +246,17 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
}
func snapBaseData(snap snapHelper, targetDir string) error {
snap.Task.Log("######################## 2 / 8 ########################")
snap.Task.Log("---------------------- 2 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
err := common.CopyFile("/usr/local/bin/1panel", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), err)
err := common.CopyFile("/usr/local/bin/1panel-core", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil {
return err
}
err = common.CopyFile("/usr/local/bin/1panel_agent", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), err)
err = common.CopyFile("/usr/local/bin/1panel-agent", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err)
if err != nil {
return err
}
@ -273,8 +273,8 @@ func snapBaseData(snap snapHelper, targetDir string) error {
return err
}
err = common.CopyFile("/etc/systemd/system/1panel_agent.service", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), err)
err = common.CopyFile("/etc/systemd/system/1panel-agent.service", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err)
if err != nil {
return err
}
@ -301,7 +301,7 @@ func snapBaseData(snap snapHelper, targetDir string) error {
}
func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) error {
snap.Task.Log("######################## 3 / 8 ########################")
snap.Task.Log("---------------------- 3 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapInstallApp"))
var imageList []string
@ -333,7 +333,7 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
}
func snapBackupData(snap snapHelper, req dto.SnapshotCreate, targetDir string) error {
snap.Task.Log("######################## 4 / 8 ########################")
snap.Task.Log("---------------------- 4 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapLocalBackup"))
excludes := loadBackupExcludes(snap, req.BackupData)
@ -389,7 +389,7 @@ func loadAppBackupExcludes(req []dto.DataTree) []string {
}
func snapPanelData(snap snapHelper, req dto.SnapshotCreate, targetDir string) error {
snap.Task.Log("######################## 5 / 8 ########################")
snap.Task.Log("---------------------- 5 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapPanelData"))
excludes := loadPanelExcludes(req.PanelData)
@ -447,7 +447,7 @@ func loadPanelExcludes(req []dto.DataTree) []string {
}
func snapCompress(snap snapHelper, rootDir string, secret string) error {
snap.Task.Log("######################## 7 / 8 ########################")
snap.Task.Log("---------------------- 7 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapCompress"))
tmpDir := path.Join(global.CONF.System.TmpDir, "system")
@ -471,7 +471,7 @@ func snapCompress(snap snapHelper, rootDir string, secret string) error {
}
func snapUpload(snap snapHelper, accounts string, file string) error {
snap.Task.Log("######################## 8 / 8 ########################")
snap.Task.Log("---------------------- 8 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapUpload"))
source := path.Join(global.CONF.System.TmpDir, "system", path.Base(file))

View File

@ -83,7 +83,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
taskItem.AddSubTaskWithAlias(
"RecoverDecompress",
func(t *task.Task) error {
itemHelper.Task.Log("######################## 2 / 10 ########################")
itemHelper.Task.Log("---------------------- 2 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverDecompress", snap.Name))
err := itemHelper.FileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.tar.gz", rootDir, snap.Name), rootDir, req.Secret)
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
@ -139,7 +139,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
taskItem.AddSubTaskWithAlias(
"RecoverBackups",
func(t *task.Task) error {
itemHelper.Task.Log("######################## 8 / 10 ########################")
itemHelper.Task.Log("---------------------- 8 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverBackups", snap.Name))
err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_backup.tar.gz"), snapJson.BackupDataDir, "")
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
@ -153,7 +153,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
taskItem.AddSubTaskWithAlias(
"RecoverPanelData",
func(t *task.Task) error {
itemHelper.Task.Log("######################## 9 / 10 ########################")
itemHelper.Task.Log("---------------------- 9 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverPanelData", snap.Name))
err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_data.tar.gz"), path.Join(snapJson.BaseDir, "1panel"), "")
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
@ -183,7 +183,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
}
func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot, targetDir string) error {
itemHelper.Task.Log("######################## 1 / 10 ########################")
itemHelper.Task.Log("---------------------- 1 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDownload"))
account, client, err := NewBackupClientWithID(snap.DownloadAccountID)
@ -200,7 +200,7 @@ func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot,
}
func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("######################## 3 / 10 ########################")
itemHelper.Task.Log("---------------------- 3 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("BackupBeforeRecover"))
rootDir := fmt.Sprintf("%s/1panel_original/original_%s", global.CONF.System.BaseDir, name)
@ -224,13 +224,13 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), err)
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-core", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel_agent", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), err)
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-agent", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err)
if err != nil {
return err
}
@ -239,8 +239,8 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel_agent.service", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), err)
err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel-agent.service", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err)
if err != nil {
return err
}
@ -253,7 +253,7 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
}
func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, error) {
itemHelper.Task.Log("######################## 4 / 10 ########################")
itemHelper.Task.Log("---------------------- 4 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("Readjson"))
snapJsonPath := path.Join(rootDir, "base/snapshot.json")
@ -277,7 +277,7 @@ func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson,
}
func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("######################## 5 / 10 ########################")
itemHelper.Task.Log("---------------------- 5 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverApp"))
if _, err := os.Stat(path.Join(src, "images.tar.gz")); err != nil {
@ -325,7 +325,7 @@ func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
}
func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("######################## 6 / 10 ########################")
itemHelper.Task.Log("---------------------- 6 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
err := itemHelper.FileOp.CopyFile(path.Join(src, "1pctl"), "/usr/local/bin")
@ -335,12 +335,12 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), err)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel_agent"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), err)
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent"), "/usr/local/bin")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err)
if err != nil {
return err
}
@ -349,8 +349,8 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel_agent.service"), "/etc/systemd/system")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), err)
err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent.service"), "/etc/systemd/system")
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err)
if err != nil {
return err
}
@ -375,7 +375,7 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
}
func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("######################## 7 / 10 ########################")
itemHelper.Task.Log("---------------------- 7 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDBData"))
err := itemHelper.FileOp.CopyDirWithExclude(src, path.Join(global.CONF.System.BaseDir, "1panel"), nil)
@ -384,7 +384,7 @@ func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
}
func restartCompose(composePath string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("######################## 10 / 10 ########################")
itemHelper.Task.Log("---------------------- 10 / 10 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverCompose"))
composes, err := composeRepo.ListRecord()

View File

@ -42,16 +42,16 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
nil,
)
taskItem.AddSubTask(
i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"),
i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"),
func(t *task.Task) error {
return FileOp.CopyFile(path.Join(baseDir, "1panel"), "/usr/local/bin")
},
nil,
)
taskItem.AddSubTask(
i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"),
i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"),
func(t *task.Task) error {
return FileOp.CopyFile(path.Join(baseDir, "1panel_agent"), "/usr/local/bin")
return FileOp.CopyFile(path.Join(baseDir, "1panel-agent"), "/usr/local/bin")
},
nil,
)
@ -63,7 +63,7 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
nil,
)
taskItem.AddSubTask(
i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"),
i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"),
func(t *task.Task) error {
return FileOp.CopyFile(path.Join(baseDir, "1panel.service"), "/etc/systemd/system")
},

View File

@ -26,6 +26,17 @@ func Init() {
if err != nil {
global.LOG.Fatalf("load current node before start failed, err: %v", err)
}
baseDir, err := settingRepo.Get(settingRepo.WithByKey("BaseDir"))
if err != nil {
global.LOG.Fatalf("load base dir before start failed, err: %v", err)
}
global.CONF.System.BaseDir = baseDir.Value
version, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
if err != nil {
global.LOG.Fatalf("load system version before start failed, err: %v", err)
}
global.CONF.System.Version = version.Value
global.IsMaster = node.Value == "127.0.0.1" || len(node.Value) == 0
if global.IsMaster {
db.InitCoreDB()

View File

@ -15,7 +15,6 @@ func Init() {
migrations.InitImageRepo,
migrations.InitDefaultCA,
migrations.InitPHPExtensions,
migrations.AddTask,
migrations.UpdateWebsite,
migrations.UpdateWebsiteDomain,
migrations.UpdateApp,
@ -23,6 +22,7 @@ func Init() {
migrations.UpdateAppInstall,
migrations.UpdateSnapshot,
migrations.UpdateCronjob,
migrations.InitBaseDir,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -53,7 +53,6 @@ var AddTable = &gormigrate.Migration{
&model.WebsiteDnsAccount{},
&model.WebsiteDomain{},
&model.WebsiteSSL{},
&model.Task{},
)
},
}
@ -78,6 +77,12 @@ var InitSetting = &gormigrate.Migration{
return err
}
global.IsMaster = isMaster
if err := tx.Create(&model.Setting{Key: "BaseDir", Value: global.CONF.System.BaseDir}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "CurrentNode", Value: currentNode}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "CurrentNode", Value: currentNode}).Error; err != nil {
return err
}
@ -211,14 +216,6 @@ var InitPHPExtensions = &gormigrate.Migration{
},
}
var AddTask = &gormigrate.Migration{
ID: "20240802-add-task",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(
&model.Task{})
},
}
var UpdateWebsite = &gormigrate.Migration{
ID: "20240812-update-website",
Migrate: func(tx *gorm.DB) error {
@ -273,3 +270,13 @@ var UpdateCronjob = &gormigrate.Migration{
return tx.AutoMigrate(&model.Cronjob{}, &model.JobRecords{})
},
}
var InitBaseDir = &gormigrate.Migration{
ID: "20241122-init-setting",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "BaseDir", Value: global.CONF.System.BaseDir}).Error; err != nil {
return err
}
return nil
},
}

View File

@ -38,9 +38,6 @@ func Init() {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
} else {
baseDir = loadParams("BASE_DIR")
version = loadParams("ORIGINAL_VERSION")
reader := bytes.NewReader(conf.AppYaml)
if err := v.ReadConfig(reader); err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))

View File

@ -6,11 +6,9 @@ import (
"path"
"sort"
"strings"
"time"
cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/files"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -45,31 +43,20 @@ var restoreCmd = &cobra.Command{
tmpPath = path.Join(upgradeDir, tmpPath, "original")
fmt.Printf("(0/4) 开始从 %s 目录回滚 1Panel 服务及数据... \n", tmpPath)
if err := files.CopyFile(path.Join(tmpPath, "1panel"), "/usr/local/bin", false); err != nil {
if err := files.CopyItem(false, true, path.Join(tmpPath, "1panel*"), "/usr/local/bin"); err != nil {
return err
}
fmt.Println("(1/4) 1panel 二进制回滚成功")
if err := files.CopyFile(path.Join(tmpPath, "1pctl"), "/usr/local/bin", false); err != nil {
if err := files.CopyItem(false, true, path.Join(tmpPath, "1pctl"), "/usr/local/bin"); err != nil {
return err
}
fmt.Println("(2/4) 1panel 脚本回滚成功")
if err := files.CopyFile(path.Join(tmpPath, "1panel.service"), "/etc/systemd/system", false); err != nil {
if err := files.CopyItem(false, true, path.Join(tmpPath, "1panel*.service"), "/etc/systemd/system"); err != nil {
return err
}
fmt.Println("(3/4) 1panel 服务回滚成功")
checkPointOfWal()
if _, err := os.Stat(path.Join(tmpPath, "core.db")); err == nil {
if err := files.CopyFile(path.Join(tmpPath, "core.db"), path.Join(baseDir, "core/db"), false); err != nil {
return err
}
}
if _, err := os.Stat(path.Join(tmpPath, "agent.db")); err == nil {
if err := files.CopyFile(path.Join(tmpPath, "agent.db"), path.Join(baseDir, "1panel/db"), false); err != nil {
return err
}
}
if _, err := os.Stat(path.Join(tmpPath, "db.tar.gz")); err == nil {
if err := handleUnTar(path.Join(tmpPath, "db.tar.gz"), path.Join(baseDir, "1panel")); err != nil {
if err := files.CopyItem(true, true, path.Join(tmpPath, "db"), path.Join(baseDir, "1panel")); err != nil {
return err
}
}
@ -80,14 +67,6 @@ var restoreCmd = &cobra.Command{
},
}
func checkPointOfWal() {
db, err := loadDBConn()
if err != nil {
return
}
_ = db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error
}
func loadRestorePath(upgradeDir string) (string, error) {
if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {
return "暂无可回滚文件", nil
@ -110,18 +89,3 @@ func loadRestorePath(upgradeDir string) (string, error) {
})
return folders[0], nil
}
func handleUnTar(sourceFile, targetDir string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
}
}
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
stdout, err := cmdUtils.ExecWithTimeOut(commands, 20*time.Second)
if err != nil {
return errors.New(stdout)
}
return nil
}

13
core/app/dto/task.go Normal file
View File

@ -0,0 +1,13 @@
package dto
import "github.com/1Panel-dev/1Panel/core/app/model"
type SearchTaskLogReq struct {
Status string `json:"status"`
Type string `json:"type"`
PageInfo
}
type TaskDTO struct {
model.Task
}

18
core/app/model/task.go Normal file
View File

@ -0,0 +1,18 @@
package model
import "time"
type Task struct {
ID string `gorm:"primarykey;" json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Operate string `json:"operate"`
LogFile string `json:"logFile"`
Status string `json:"status"`
ErrorMsg string `json:"errorMsg"`
OperationLogID uint `json:"operationLogID"`
ResourceID uint `json:"resourceID"`
CurrentStep string `json:"currentStep"`
EndAt time.Time `json:"endAt"`
CreatedAt time.Time `json:"createdAt"`
}

View File

@ -0,0 +1,9 @@
package model
type UpgradeLog struct {
BaseModel
NodeID uint `json:"nodeID"`
OldVersion string `json:"oldVersion"`
NewVersion string `json:"newVersion"`
BackupFile string `json:"backupFile"`
}

View File

@ -17,6 +17,7 @@ type ICommonRepo interface {
WithByType(ty string) DBOption
WithByKey(key string) DBOption
WithOrderBy(orderStr string) DBOption
WithByStatus(status string) DBOption
WithOrderRuleBy(orderBy, order string) DBOption
}
@ -66,6 +67,11 @@ func (c *CommonRepo) WithByKey(key string) DBOption {
return g.Where("key = ?", key)
}
}
func (c *CommonRepo) WithByStatus(status string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("status = ?", status)
}
}
func (c *CommonRepo) WithOrderBy(orderStr string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Order(orderStr)

90
core/app/repo/task.go Normal file
View File

@ -0,0 +1,90 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/global"
"gorm.io/gorm"
)
type TaskRepo struct {
}
type ITaskRepo interface {
Save(ctx context.Context, task *model.Task) error
GetFirst(opts ...DBOption) (model.Task, error)
Page(page, size int, opts ...DBOption) (int64, []model.Task, error)
Update(ctx context.Context, task *model.Task) error
WithByID(id string) DBOption
WithResourceID(id uint) DBOption
WithOperate(taskOperate string) DBOption
}
func NewITaskRepo() ITaskRepo {
return &TaskRepo{}
}
func getTaskDb(opts ...DBOption) *gorm.DB {
db := global.TaskDB
for _, opt := range opts {
db = opt(db)
}
return db
}
func getTaskTx(ctx context.Context, opts ...DBOption) *gorm.DB {
tx, ok := ctx.Value("db").(*gorm.DB)
if ok {
for _, opt := range opts {
tx = opt(tx)
}
return tx
}
return getTaskDb(opts...)
}
func (t TaskRepo) WithByID(id string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id = ?", id)
}
}
func (t TaskRepo) WithOperate(taskOperate string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("operate = ?", taskOperate)
}
}
func (t TaskRepo) WithResourceID(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("resource_id = ?", id)
}
}
func (t TaskRepo) Save(ctx context.Context, task *model.Task) error {
return getTaskTx(ctx).Save(&task).Error
}
func (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) {
var task model.Task
db := getTaskDb(opts...).Model(&model.Task{})
if err := db.First(&task).Error; err != nil {
return task, err
}
return task, nil
}
func (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, error) {
var tasks []model.Task
db := getTaskDb(opts...).Model(&model.Task{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error
return count, tasks, err
}
func (t TaskRepo) Update(ctx context.Context, task *model.Task) error {
return getTaskTx(ctx).Save(&task).Error
}

View File

@ -0,0 +1,93 @@
package repo
import (
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/global"
"gorm.io/gorm"
)
type UpgradeLogRepo struct{}
type IUpgradeLogRepo interface {
Get(opts ...DBOption) (model.UpgradeLog, error)
List(opts ...DBOption) ([]model.UpgradeLog, error)
Create(log *model.UpgradeLog) error
Page(limit, offset int, opts ...DBOption) (int64, []model.UpgradeLog, error)
Delete(opts ...DBOption) error
WithByNodeID(nodeID uint) DBOption
WithByIDs(ids []uint) DBOption
WithByID(id uint) DBOption
}
func NewIUpgradeLogRepo() IUpgradeLogRepo {
return &UpgradeLogRepo{}
}
func (u *UpgradeLogRepo) Get(opts ...DBOption) (model.UpgradeLog, error) {
var log model.UpgradeLog
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&log).Error
return log, err
}
func (u *UpgradeLogRepo) List(opts ...DBOption) ([]model.UpgradeLog, error) {
var logs []model.UpgradeLog
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&logs).Error
return logs, err
}
func (u *UpgradeLogRepo) Clean() error {
return global.DB.Exec("delete from upgrade_logs;").Error
}
func (u *UpgradeLogRepo) Create(log *model.UpgradeLog) error {
return global.DB.Create(log).Error
}
func (u *UpgradeLogRepo) Save(log *model.UpgradeLog) error {
return global.DB.Save(log).Error
}
func (u *UpgradeLogRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.UpgradeLog{}).Error
}
func (u *UpgradeLogRepo) Page(page, size int, opts ...DBOption) (int64, []model.UpgradeLog, error) {
var ops []model.UpgradeLog
db := global.DB.Model(&model.UpgradeLog{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
return count, ops, err
}
func (c *UpgradeLogRepo) WithByNodeID(nodeID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("node_id = ?", nodeID)
}
}
func (c *UpgradeLogRepo) WithByID(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id = ?", id)
}
}
func (c *UpgradeLogRepo) WithByIDs(ids []uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id in (?)", ids)
}
}

View File

@ -11,4 +11,6 @@ var (
logRepo = repo.NewILogRepo()
groupRepo = repo.NewIGroupRepo()
launcherRepo = repo.NewILauncherRepo()
taskRepo = repo.NewITaskRepo()
)

42
core/app/service/task.go Normal file
View File

@ -0,0 +1,42 @@
package service
import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/repo"
)
type TaskLogService struct{}
type ITaskLogService interface {
Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error)
}
func NewITaskService() ITaskLogService {
return &TaskLogService{}
}
func (u *TaskLogService) Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error) {
opts := []repo.DBOption{
commonRepo.WithOrderBy("created_at desc"),
}
if req.Status != "" {
opts = append(opts, commonRepo.WithByStatus(req.Status))
}
if req.Type != "" {
opts = append(opts, commonRepo.WithByType(req.Type))
}
total, tasks, err := taskRepo.Page(
req.Page,
req.PageSize,
opts...,
)
var items []dto.TaskDTO
for _, t := range tasks {
item := dto.TaskDTO{
Task: t,
}
items = append(items, item)
}
return total, items, err
}

View File

@ -83,8 +83,10 @@ func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
global.LOG.Info("start to upgrade now...")
timeStr := time.Now().Format(constant.DateTimeSlimLayout)
rootDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/tmp/upgrade/upgrade_%s/downloads", timeStr))
originalDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/tmp/upgrade/upgrade_%s/original", timeStr))
baseDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/tmp/upgrade/%s", req.Version))
rootDir := path.Join(baseDir, fmt.Sprintf("upgrade_%s/downloads", timeStr))
_ = os.RemoveAll(baseDir)
originalDir := path.Join(baseDir, fmt.Sprintf("upgrade_%s/original", timeStr))
if err := os.MkdirAll(rootDir, os.ModePerm); err != nil {
return err
}
@ -127,13 +129,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
}
global.LOG.Info("backup original data successful, now start to upgrade!")
if err := files.CopyFile(path.Join(tmpDir, "1panel"), "/usr/local/bin", false); err != nil {
if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel*"), "/usr/local/bin"); err != nil {
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
u.handleRollback(originalDir, 1)
return
}
if err := files.CopyFile(path.Join(tmpDir, "1pctl"), "/usr/local/bin", false); err != nil {
if err := files.CopyItem(false, true, path.Join(tmpDir, "1pctl"), "/usr/local/bin"); err != nil {
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
u.handleRollback(originalDir, 2)
return
@ -144,7 +146,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
return
}
if err := files.CopyFile(path.Join(tmpDir, "1panel.service"), "/etc/systemd/system", false); err != nil {
if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel*.service"), "/etc/systemd/system"); err != nil {
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
u.handleRollback(originalDir, 3)
return
@ -154,24 +156,22 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
go writeLogs(req.Version)
_ = settingRepo.Update("SystemVersion", req.Version)
_ = settingRepo.Update("SystemStatus", "Free")
checkPointOfWal()
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute)
}()
return nil
}
func (u *UpgradeService) handleBackup(originalDir string) error {
if err := files.CopyFile("/usr/local/bin/1panel", originalDir, false); err != nil {
if err := files.CopyItem(false, true, "/usr/local/bin/1panel*", originalDir); err != nil {
return err
}
if err := files.CopyFile("/usr/local/bin/1pctl", originalDir, false); err != nil {
if err := files.CopyItem(false, true, "/usr/local/bin/1pctl", originalDir); err != nil {
return err
}
if err := files.CopyFile("/etc/systemd/system/1panel.service", originalDir, false); err != nil {
if err := files.CopyItem(false, true, "/etc/systemd/system/1panel*.service", originalDir); err != nil {
return err
}
checkPointOfWal()
if err := files.HandleTar(path.Join(global.CONF.System.BaseDir, "1panel/db"), originalDir, "db.tar.gz", "db/1Panel.db-*", ""); err != nil {
if err := files.CopyItem(true, true, path.Join(global.CONF.System.BaseDir, "1panel/db"), originalDir); err != nil {
return err
}
return nil
@ -180,31 +180,25 @@ func (u *UpgradeService) handleBackup(originalDir string) error {
func (u *UpgradeService) handleRollback(originalDir string, errStep int) {
_ = settingRepo.Update("SystemStatus", "Free")
checkPointOfWal()
dbPath := path.Join(global.CONF.System.BaseDir, "1panel/db")
if _, err := os.Stat(path.Join(originalDir, "1Panel.db")); err == nil {
if err := files.CopyFile(path.Join(originalDir, "1Panel.db"), dbPath, false); err != nil {
if _, err := os.Stat(path.Join(originalDir, "db")); err == nil {
if err := files.CopyItem(true, true, path.Join(originalDir, "db"), dbPath); err != nil {
global.LOG.Errorf("rollback 1panel db failed, err: %v", err)
}
}
if _, err := os.Stat(path.Join(originalDir, "db.tar.gz")); err == nil {
if err := files.HandleUnTar(path.Join(originalDir, "db.tar.gz"), dbPath, ""); err != nil {
global.LOG.Errorf("rollback 1panel db failed, err: %v", err)
}
}
if err := files.CopyFile(path.Join(originalDir, "1panel"), "/usr/local/bin", false); err != nil {
if err := files.CopyItem(false, true, path.Join(originalDir, "1panel*"), "/usr/local/bin"); err != nil {
global.LOG.Errorf("rollback 1pctl failed, err: %v", err)
}
if errStep == 1 {
return
}
if err := files.CopyFile(path.Join(originalDir, "1pctl"), "/usr/local/bin", false); err != nil {
if err := files.CopyItem(false, true, path.Join(originalDir, "1pctl"), "/usr/local/bin"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
if errStep == 2 {
return
}
if err := files.CopyFile(path.Join(originalDir, "1panel.service"), "/etc/systemd/system", false); err != nil {
if err := files.CopyItem(false, true, path.Join(originalDir, "1panel*.service"), "/etc/systemd/system"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
}
@ -345,9 +339,3 @@ func loadArch() (string, error) {
}
return "", fmt.Errorf("unsupported such arch: %s", std)
}
func checkPointOfWal() {
if err := global.DB.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil {
global.LOG.Errorf("handle check point failed, err: %v", err)
}
}

253
core/app/task/task.go Normal file
View File

@ -0,0 +1,253 @@
package task
import (
"context"
"fmt"
"log"
"os"
"path"
"strconv"
"time"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/i18n"
"github.com/google/uuid"
)
type ActionFunc func(*Task) error
type RollbackFunc func(*Task)
type Task struct {
Name string
TaskID string
Logger *log.Logger
SubTasks []*SubTask
Rollbacks []RollbackFunc
logFile *os.File
taskRepo repo.ITaskRepo
Task *model.Task
ParentID string
}
type SubTask struct {
RootTask *Task
Name string
StepAlias string
Retry int
Timeout time.Duration
Action ActionFunc
Rollback RollbackFunc
Error error
IgnoreErr bool
}
const (
TaskUpgrade = "TaskUpgrade"
TaskAddNode = "TaskAddNode"
)
const (
TaskScopeSystem = "System"
)
const (
TaskSuccess = "Success"
TaskFailed = "Failed"
)
func GetTaskName(resourceName, operate, scope string) string {
return fmt.Sprintf("%s%s [%s]", i18n.GetMsgByKey(operate), i18n.GetMsgByKey(scope), resourceName)
}
func NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {
return NewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)
}
func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {
if taskID == "" {
taskID = uuid.New().String()
}
logItem := path.Join(global.CONF.System.BaseDir, "1panel/log")
logDir := path.Join(logItem, taskScope)
if _, err := os.Stat(logDir); os.IsNotExist(err) {
if err = os.MkdirAll(logDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
}
logPath := path.Join(logItem, taskScope, taskID+".log")
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
logger := log.New(file, "", log.LstdFlags)
taskModel := &model.Task{
ID: taskID,
Name: name,
Type: taskScope,
LogFile: logPath,
Status: constant.StatusRunning,
ResourceID: resourceID,
Operate: operate,
}
taskRepo := repo.NewITaskRepo()
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel}
return task, nil
}
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}
t.SubTasks = append(t.SubTasks, subTask)
}
func (t *Task) AddSubTaskWithAlias(key string, action ActionFunc, rollback RollbackFunc) {
subTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}
t.SubTasks = append(t.SubTasks, subTask)
}
func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
subTask := &SubTask{RootTask: t, Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
t.SubTasks = append(t.SubTasks, subTask)
}
func (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) {
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: nil, IgnoreErr: true}
t.SubTasks = append(t.SubTasks, subTask)
}
func (s *SubTask) Execute() error {
subTaskName := s.Name
if s.Name == "" {
subTaskName = i18n.GetMsgByKey("SubTask")
}
var err error
for i := 0; i < s.Retry+1; i++ {
if i > 0 {
s.RootTask.Log(i18n.GetWithName("TaskRetry", strconv.Itoa(i)))
}
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
defer cancel()
done := make(chan error)
go func() {
done <- s.Action(s.RootTask)
}()
select {
case <-ctx.Done():
s.RootTask.Log(i18n.GetWithName("TaskTimeout", subTaskName))
case err = <-done:
if err != nil {
s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", subTaskName, err))
} else {
s.RootTask.Log(i18n.GetWithName("SubTaskSuccess", subTaskName))
return nil
}
}
if i == s.Retry {
if s.Rollback != nil {
s.Rollback(s.RootTask)
}
}
time.Sleep(1 * time.Second)
}
return err
}
func (t *Task) updateTask(task *model.Task) {
_ = t.taskRepo.Update(context.Background(), task)
}
func (t *Task) Execute() error {
if err := t.taskRepo.Save(context.Background(), t.Task); err != nil {
return err
}
var err error
t.Log(i18n.GetWithName("TaskStart", t.Name))
for _, subTask := range t.SubTasks {
t.Task.CurrentStep = subTask.StepAlias
t.updateTask(t.Task)
if err = subTask.Execute(); err == nil {
if subTask.Rollback != nil {
t.Rollbacks = append(t.Rollbacks, subTask.Rollback)
}
} else {
if subTask.IgnoreErr {
err = nil
continue
}
t.Task.ErrorMsg = err.Error()
t.Task.Status = constant.StatusFailed
for _, rollback := range t.Rollbacks {
rollback(t)
}
t.updateTask(t.Task)
break
}
}
if t.Task.Status == constant.StatusRunning {
t.Task.Status = constant.StatusSuccess
t.Log(i18n.GetWithName("TaskSuccess", t.Name))
} else {
t.Log(i18n.GetWithName("TaskFailed", t.Name))
}
t.Log("[TASK-END]")
t.Task.EndAt = time.Now()
t.updateTask(t.Task)
_ = t.logFile.Close()
return err
}
func (t *Task) DeleteLogFile() {
_ = os.Remove(t.Task.LogFile)
}
func (t *Task) LogWithStatus(msg string, err error) {
if err != nil {
t.Logger.Printf(i18n.GetWithNameAndErr("FailedStatus", msg, err))
} else {
t.Logger.Printf(i18n.GetWithName("SuccessStatus", msg))
}
}
func (t *Task) Log(msg string) {
t.Logger.Printf(msg)
}
func (t *Task) Logf(format string, v ...any) {
t.Logger.Printf(format, v...)
}
func (t *Task) LogFailed(msg string) {
t.Logger.Printf(msg + i18n.GetMsgByKey("Failed"))
}
func (t *Task) LogFailedWithErr(msg string, err error) {
t.Logger.Printf(fmt.Sprintf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error()))
}
func (t *Task) LogSuccess(msg string) {
t.Logger.Printf(msg + i18n.GetMsgByKey("Success"))
}
func (t *Task) LogSuccessF(format string, v ...any) {
t.Logger.Printf(fmt.Sprintf(format, v...) + i18n.GetMsgByKey("Success"))
}
func (t *Task) LogStart(msg string) {
t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg))
}
func (t *Task) LogWithOps(operate, msg string) {
t.Logger.Printf("%s%s", i18n.GetMsgByKey(operate), msg)
}
func (t *Task) LogSuccessWithOps(operate, msg string) {
t.Logger.Printf("%s%s%s", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Success"))
}
func (t *Task) LogFailedWithOps(operate, msg string, err error) {
t.Logger.Printf("%s%s%s : %s ", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Failed"), err.Error())
}

View File

@ -12,4 +12,6 @@ const (
StatusStarting = "starting"
StatusHealthy = "healthy"
StatusUnhealthy = "unhealthy"
StatusUpgrading = "upgrading"
StatusRunning = "running"
)

View File

@ -15,6 +15,7 @@ import (
var (
DB *gorm.DB
TaskDB *gorm.DB
LOG *logrus.Logger
CONF configs.ServerConfig
VALID *validator.Validate

View File

@ -32,25 +32,20 @@ func GetMsgWithMap(key string, maps map[string]interface{}) string {
}
}
func GetMsgWithName(key string, name string, err error) string {
func GetMsgWithDetail(key string, detail string) string {
var (
content string
dataMap = make(map[string]interface{})
)
dataMap["name"] = name
if err != nil {
dataMap["err"] = err.Error()
}
dataMap["detail"] = detail
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
TemplateData: dataMap,
})
content = strings.ReplaceAll(content, "<no value>", "")
if content == "" {
return key
} else {
if content != "" {
return content
}
return key
}
func GetErrMsg(key string, maps map[string]interface{}) string {
@ -75,6 +70,41 @@ func GetMsgByKey(key string) string {
return content
}
func Get(key string) string {
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
})
if content != "" {
return content
}
return key
}
func GetWithName(key string, name string) string {
var (
dataMap = make(map[string]interface{})
)
dataMap["name"] = name
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
TemplateData: dataMap,
})
return content
}
func GetWithNameAndErr(key string, name string, err error) string {
var (
dataMap = make(map[string]interface{})
)
dataMap["name"] = name
dataMap["err"] = err.Error()
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
TemplateData: dataMap,
})
return content
}
//go:embed lang/*
var fs embed.FS
var bundle *i18n.Bundle

View File

@ -29,3 +29,42 @@ ErrNoSuchHost: "Network connection failed"
ErrBackupInUsed: "The backup account is currently in use in a scheduled task and cannot be deleted."
ErrBackupCheck: "Backup account test connection failed {{.err}}"
ErrBackupLocalDelete: "Deleting local server backup accounts is not currently supported."
#task
TaskStart: "{{.name}} Task Start [START]"
TaskEnd: "{{.name}} Task End [COMPLETED]"
TaskFailed: "{{.name}} Task Failed"
TaskTimeout: "{{.name}} Timeout"
TaskSuccess: "{{.name}} Task Successful"
TaskRetry: "Starting the {{.name}} retry"
SubTaskSuccess: "{{ .name }} Successful"
SubTaskFailed: "{{ .name }} Failed: {{ .err }}"
TaskInstall: "Install"
TaskUninstall: "Uninstall"
TaskCreate: "Create"
TaskDelete: "Delete"
TaskUpgrade: "Upgrade"
TaskUpdate: "Update"
TaskRestart: "Restart"
TaskRollback: "Rollback"
SuccessStatus: "{{ .name }} Successful"
FailedStatus: "{{ .name }} Failed {{.err}}"
PullImage: "Pull Image"
Start: "Start"
Run: "Launch"
Stop: "Stop"
SubTask: "Subtask"
#upgrade node
NodeUpgrade: "Upgrade Node {name}"
NewSSHClient: "Initialize SSH Connection"
BackupBeforeUpgrade: "Backup Data Before Upgrade"
UploadUpgradeFile: "Distribute Upgrade Required Files"
RestartAfterUpgrade: "Start Service After Upgrade"
#add node
TaskAddNode: "Add Node"
GenerateSSLInfo: "Generate Node SSL Information"
MakeAgentPackage: "Generate Node Installation Package"
SendAgent: "Distribute Node Installation Package"
StartService: "Start Service"

View File

@ -28,4 +28,43 @@ ErrNoSuchHost: "網路連接失敗"
#backup
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
ErrBackupCheck: "備份帳號測試連接失敗 {{.err}}"
ErrBackupLocalDelete: "暫不支持刪除本地伺服器備份帳號"
ErrBackupLocalDelete: "暫不支持刪除本地伺服器備份帳號"
#task
TaskStart: "{{.name}} 任務開始 [START]"
TaskEnd: "{{.name}} 任務結束 [COMPLETED]"
TaskFailed: "{{.name}} 任務失敗"
TaskTimeout: "{{.name}} 超時"
TaskSuccess: "{{.name}} 任務成功"
TaskRetry: "開始第 {{.name}} 次重試"
SubTaskSuccess: "{{ .name }} 成功"
SubTaskFailed: "{{ .name }} 失敗: {{ .err }}"
TaskInstall: "安裝"
TaskUninstall: "卸載"
TaskCreate: "創建"
TaskDelete: "刪除"
TaskUpgrade: "升級"
TaskUpdate: "更新"
TaskRestart: "重啟"
TaskRollback: "回滾"
SuccessStatus: "{{ .name }} 成功"
FailedStatus: "{{ .name }} 失敗 {{.err}}"
PullImage: "拉取鏡像"
Start: "開始"
Run: "啟動"
Stop: "停止"
SubTask: "子任務"
#node upgrade
NodeUpgrade: "升級節點 {name}"
NewSSHClient: "初始化 SSH 連接"
BackupBeforeUpgrade: "升級前備份數據"
UploadUpgradeFile: "下發升級所需文件"
RestartAfterUpgrade: "升級後啟動服務"
#node create
TaskAddNode: "添加節點"
GenerateSSLInfo: "生成節點 SSL 信息"
MakeAgentPackage: "生成節點安裝包"
SendAgent: "下發節點安裝包"
StartService: "啟動服務"

View File

@ -30,4 +30,44 @@ ErrNoSuchHost: "网络连接失败"
#backup
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
ErrBackupCheck: "备份账号测试连接失败 {{ .err}}"
ErrBackupLocalDelete: "暂不支持删除本地服务器备份账号"
ErrBackupLocalDelete: "暂不支持删除本地服务器备份账号"
#task
#task
TaskStart: "{{.name}} 任务开始 [START]"
TaskEnd: "{{.name}} 任务结束 [COMPLETED]"
TaskFailed: "{{.name}} 任务失败"
TaskTimeout: "{{.name}} 超时"
TaskSuccess: "{{.name}} 任务成功"
TaskRetry: "开始第 {{.name}} 次重试"
SubTaskSuccess: "{{ .name }} 成功"
SubTaskFailed: "{{ .name }} 失败: {{ .err }}"
TaskInstall: "安装"
TaskUninstall: "卸载"
TaskCreate: "创建"
TaskDelete: "删除"
TaskUpgrade: "升级"
TaskUpdate: "更新"
TaskRestart: "重启"
TaskRollback: "回滚"
SuccessStatus: "{{ .name }} 成功"
FailedStatus: "{{ .name }} 失败 {{.err}}"
PullImage: "拉取镜像"
Start: "开始"
Run: "启动"
Stop: "停止"
SubTask: "子任务"
#upgrade node
NodeUpgrade: "升级节点 {name}"
NewSSHClient: "初始化 SSH 连接"
BackupBeforeUpgrade: "升级前备份数据"
UploadUpgradeFile: "下发升级所需文件"
RestartAfterUpgrade: "升级后启动服务"
#add node
TaskAddNode: "添加节点"
GenerateSSLInfo: "生成节点 SSL 信息"
MakeAgentPackage: "生成节点安装包"
SendAgent: "下发节点安装包"
StartService: "启动服务"

View File

@ -14,6 +14,11 @@ import (
)
func Init() {
initDB()
initTaskDB()
}
func initDB() {
dbPath := path.Join(global.CONF.System.BaseDir, "1panel/db")
if _, err := os.Stat(dbPath); err != nil {
if err := os.MkdirAll(dbPath, os.ModePerm); err != nil {
@ -29,7 +34,50 @@ func Init() {
_ = f.Close()
}
newLogger := logger.New(
db, err := NewDBWithPath(fullPath)
if err != nil {
panic(err)
}
global.DB = db
global.LOG.Info("init db successfully")
}
func initTaskDB() {
fullPath := path.Join(global.CONF.System.BaseDir, "1panel/db/task.db")
if _, err := os.Stat(fullPath); err != nil {
f, err := os.Create(fullPath)
if err != nil {
panic(fmt.Errorf("init task db file failed, err: %v", err))
}
_ = f.Close()
}
db, err := NewDBWithPath(fullPath)
if err != nil {
panic(err)
}
global.TaskDB = db
global.LOG.Info("init task db successfully")
}
func NewDBWithPath(dbPath string) (*gorm.DB, error) {
db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: getLogger(),
})
sqlDB, dbError := db.DB()
if dbError != nil {
return nil, dbError
}
sqlDB.SetConnMaxIdleTime(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
return db, nil
}
func getLogger() logger.Interface {
return logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
@ -38,22 +86,4 @@ func Init() {
Colorful: false,
},
)
db, err := gorm.Open(sqlite.Open(fullPath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: newLogger,
})
if err != nil {
panic(err)
}
sqlDB, dbError := db.DB()
if dbError != nil {
panic(dbError)
}
sqlDB.SetConnMaxIdleTime(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
global.DB = db
global.LOG.Info("init db successfully")
}

View File

@ -40,6 +40,11 @@ func Init() {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
}
global.CONF.System.SSL = sslSetting.Value
versionSetting, err := settingRepo.Get(commonRepo.WithByKey("SystemVersion"))
if err != nil {
global.LOG.Errorf("load version from setting failed, err: %v", err)
}
global.CONF.System.Version = versionSetting.Value
if _, err := settingRepo.Get(commonRepo.WithByKey("SystemStatus")); err != nil {
_ = settingRepo.Create("SystemStatus", "Free")

View File

@ -18,6 +18,7 @@ func Init() {
migrations.InitAppLauncher,
migrations.InitBackup,
migrations.InitGoogle,
migrations.AddTaskDB,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -15,7 +15,7 @@ import (
)
var AddTable = &gormigrate.Migration{
ID: "20240819-add-table",
ID: "20241224-add-table",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(
&model.OperationLog{},
@ -25,6 +25,7 @@ var AddTable = &gormigrate.Migration{
&model.Group{},
&model.Host{},
&model.Command{},
&model.UpgradeLog{},
)
},
}
@ -260,3 +261,12 @@ var InitGoogle = &gormigrate.Migration{
return nil
},
}
var AddTaskDB = &gormigrate.Migration{
ID: "20241125-add-task-table",
Migrate: func(tx *gorm.DB) error {
return global.TaskDB.AutoMigrate(
&model.Task{},
)
},
}

View File

@ -47,6 +47,30 @@ func CopyFile(src, dst string, withName bool) error {
return nil
}
func CopyItem(isDir, withName bool, src, dst string) error {
if path.Base(src) != path.Base(dst) && !withName {
dst = path.Join(dst, path.Base(src))
}
srcInfo, err := os.Stat(path.Dir(src))
if err != nil {
return err
}
if _, err := os.Stat(path.Dir(dst)); err != nil {
if os.IsNotExist(err) {
_ = os.MkdirAll(path.Dir(dst), srcInfo.Mode())
}
}
cmdStr := fmt.Sprintf(`cp -rf %s %s`, src, dst+"/")
if !isDir {
cmdStr = fmt.Sprintf(`cp -f %s %s`, src, dst+"/")
}
stdout, err := cmd.Exec(cmdStr)
if err != nil {
return fmt.Errorf("handle %s failed, stdout: %s, err: %v", cmdStr, stdout, err)
}
return nil
}
func HandleTar(sourceDir, targetDir, name, exclusionRules string, secret string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {

View File

@ -71,6 +71,17 @@ func (c *SSHClient) Run(shell string) (string, error) {
return string(buf), err
}
func (c *SSHClient) Runf(shell string, args ...interface{}) (string, error) {
session, err := c.Client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
buf, err := session.CombinedOutput(fmt.Sprintf(shell, args...))
return string(buf), err
}
func (c *SSHClient) Close() {
_ = c.Client.Close()
}

View File

@ -130,11 +130,11 @@ export const loadSnapshotSize = (param: SearchWithPage) => {
// upgrade
export const loadUpgradeInfo = () => {
return http.get<Setting.UpgradeInfo>(`/settings/upgrade`);
return http.get<Setting.UpgradeInfo>(`/core/settings/upgrade`);
};
export const loadReleaseNotes = (version: string) => {
return http.post<string>(`/settings/upgrade/notes`, { version: version });
return http.post<string>(`/core/settings/upgrade/notes`, { version: version });
};
export const upgrade = (version: string) => {
return http.post(`/settings/upgrade`, { version: version });
return http.post(`/core/settings/upgrade`, { version: version });
};