From f96e633de875f9e112fe86ec14d09efd6eb524a7 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Thu, 21 Nov 2024 18:03:27 +0800 Subject: [PATCH] feat(system-upgrade): Added support for multi-host upgrade --- .gitignore | 4 +- Makefile | 4 +- agent/app/api/v2/entry.go | 2 + agent/app/api/v2/upgrade.go | 33 +++++++ agent/app/dto/upgrade.go | 10 ++ agent/app/service/snapshot_create.go | 28 +++--- agent/app/service/snapshot_recover.go | 42 ++++---- agent/app/service/snapshot_rollback.go | 8 +- agent/app/service/upgrade.go | 126 ++++++++++++++++++++++++ agent/init/hook/hook.go | 11 +++ agent/init/migration/migrations/init.go | 9 ++ agent/init/viper/viper.go | 3 - agent/router/common.go | 1 + agent/router/upgrade.go | 17 ++++ cmd/server/cmd/restore.go | 44 +-------- core/app/service/upgrade.go | 44 +++------ core/constant/status.go | 1 + core/utils/files/files.go | 24 +++++ core/utils/ssh/ssh.go | 11 +++ frontend/src/api/modules/setting.ts | 6 +- 20 files changed, 311 insertions(+), 117 deletions(-) create mode 100644 agent/app/api/v2/upgrade.go create mode 100644 agent/app/dto/upgrade.go create mode 100644 agent/app/service/upgrade.go create mode 100644 agent/router/upgrade.go diff --git a/.gitignore b/.gitignore index 0c5231617..e1f13e39a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ *.dll *.so *.dylib -build/1panel_agent -build/1panel_core +build/1panel-agent +build/1panel-core # Mac .DS_Store diff --git a/Makefile b/Makefile index cfb37dd3b..cca042662 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/agent/app/api/v2/entry.go b/agent/app/api/v2/entry.go index 972ca2ca8..f191b9c79 100644 --- a/agent/app/api/v2/entry.go +++ b/agent/app/api/v2/entry.go @@ -64,4 +64,6 @@ var ( websiteCAService = service.NewIWebsiteCAService() taskService = service.NewITaskService() + + upgradeService = service.NewIUpgradeService() ) diff --git a/agent/app/api/v2/upgrade.go b/agent/app/api/v2/upgrade.go new file mode 100644 index 000000000..72cf69e9a --- /dev/null +++ b/agent/app/api/v2/upgrade.go @@ -0,0 +1,33 @@ +package v2 + +import ( + "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) Upgrade(c *gin.Context) { + var req dto.Upgrade + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := upgradeService.Upgrade(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) Rollback(c *gin.Context) { + var req dto.Rollback + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := upgradeService.Rollback(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.SuccessWithData(c, nil) +} diff --git a/agent/app/dto/upgrade.go b/agent/app/dto/upgrade.go new file mode 100644 index 000000000..8bd538a49 --- /dev/null +++ b/agent/app/dto/upgrade.go @@ -0,0 +1,10 @@ +package dto + +type Upgrade struct { + Version string `json:"version"` + UpgradePath string `json:"upgradePath"` +} + +type Rollback struct { + Version string `json:"version"` +} diff --git a/agent/app/service/snapshot_create.go b/agent/app/service/snapshot_create.go index 793674678..690e22b5c 100644 --- a/agent/app/service/snapshot_create.go +++ b/agent/app/service/snapshot_create.go @@ -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)) diff --git a/agent/app/service/snapshot_recover.go b/agent/app/service/snapshot_recover.go index b25019200..5b737bada 100644 --- a/agent/app/service/snapshot_recover.go +++ b/agent/app/service/snapshot_recover.go @@ -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() diff --git a/agent/app/service/snapshot_rollback.go b/agent/app/service/snapshot_rollback.go index 5af322e68..1fab2615b 100644 --- a/agent/app/service/snapshot_rollback.go +++ b/agent/app/service/snapshot_rollback.go @@ -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") }, diff --git a/agent/app/service/upgrade.go b/agent/app/service/upgrade.go new file mode 100644 index 000000000..7db74c0fe --- /dev/null +++ b/agent/app/service/upgrade.go @@ -0,0 +1,126 @@ +package service + +import ( + "fmt" + "os" + "path" + "sort" + "time" + + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/files" +) + +type UpgradeService struct{} + +type IUpgradeService interface { + Upgrade(req dto.Upgrade) error + Rollback(req dto.Rollback) error +} + +func NewIUpgradeService() IUpgradeService { + return &UpgradeService{} +} + +func (u *UpgradeService) Upgrade(req dto.Upgrade) error { + fileOP := files.NewFileOp() + if !fileOP.Stat(req.UpgradePath) { + global.LOG.Errorf("handle upgrade 1panel to %s failed, err: no such file %s", req.Version, req.UpgradePath) + return fmt.Errorf("no such upgrade file %s", req.UpgradePath) + } + + _, _ = cmd.Execf("rm -rf %s", path.Join(global.CONF.System.BaseDir, "1panel/tmp/upgrade", req.Version, "upgrade_*")) + + backupDir := path.Join(global.CONF.System.BaseDir, "1panel/tmp/upgrade", req.Version, fmt.Sprintf("upgrade_%s", time.Now().Format(constant.DateTimeSlimLayout))) + _ = os.MkdirAll(backupDir, os.ModePerm) + if err := u.handleBackup(backupDir, fileOP); err != nil { + return err + } + if err := fileOP.CopyFile(path.Join(req.UpgradePath, "1panel-agent.service"), "/etc/systemd/system/1panel-agent.service"); err != nil { + _ = u.handleRollback(backupDir, 1, fileOP) + global.LOG.Errorf("handle upgrade 1panel service file failed, err: %v", err) + return err + } + if err := fileOP.CopyFile(path.Join(req.UpgradePath, "1panel-agent"), "/usr/local/bin"); err != nil { + _ = u.handleRollback(backupDir, 2, fileOP) + global.LOG.Errorf("handle upgrade 1panel failed, err: %v", err) + return err + } + global.LOG.Info("upgrade successful!") + _ = settingRepo.Update("SystemVersion", req.Version) + _, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute) + return nil +} + +func (u *UpgradeService) Rollback(req dto.Rollback) error { + rollbackDir := path.Join(global.CONF.System.BaseDir, "1panel/tmp/upgrade", req.Version) + pathItem := loadRestorePath(rollbackDir) + if len(pathItem) == 0 { + return fmt.Errorf("no such rollback file in %s", rollbackDir) + } + fileOP := files.NewFileOp() + return u.handleRollback(pathItem, 2, fileOP) +} + +func (u *UpgradeService) handleBackup(backupDir string, fileOP files.FileOp) error { + if err := fileOP.CopyDir(path.Join(global.CONF.System.BaseDir, "1panel/db"), backupDir); err != nil { + global.LOG.Errorf("handle backup original db file failed, err: %v", err) + return err + } + if err := fileOP.CopyFile("/usr/local/bin/1panel-agent", backupDir); err != nil { + global.LOG.Errorf("handle backup 1panel binary file failed, err: %v", err) + return err + } + if err := fileOP.CopyFile("/etc/systemd/system/1panel-agent.service", backupDir); err != nil { + global.LOG.Errorf("handle backup 1panel service file failed, err: %v", err) + return err + } + return nil +} + +func (u *UpgradeService) handleRollback(backupDir string, errStep int, fileOP files.FileOp) error { + if errStep == 1 { + if err := fileOP.CopyFile(path.Join(backupDir, "1panel-agent.service"), "/etc/systemd/system/1panel-agent.service"); err != nil { + global.LOG.Errorf("handle recover 1panel service file failed, err: %v", err) + return err + } + return nil + } + if errStep == 2 { + if err := fileOP.CopyFile(path.Join(backupDir, "1panel-agent"), "/usr/local/bin/1panel-agent"); err != nil { + global.LOG.Errorf("handle recover 1panel service file failed, err: %v", err) + return err + } + if err := fileOP.CopyDir(path.Join(backupDir, "db"), path.Join(global.CONF.System.BaseDir, "1panel")); err != nil { + global.LOG.Errorf("handle recover 1panel db file failed, err: %v", err) + return err + } + } + return nil +} + +func loadRestorePath(upgradeDir string) string { + if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) { + return "" + } + files, err := os.ReadDir(upgradeDir) + if err != nil { + return "" + } + var folders []string + for _, file := range files { + if file.IsDir() { + folders = append(folders, file.Name()) + } + } + if len(folders) == 0 { + return "" + } + sort.Slice(folders, func(i, j int) bool { + return folders[i] > folders[j] + }) + return folders[0] +} diff --git a/agent/init/hook/hook.go b/agent/init/hook/hook.go index 9ec292652..df16a6404 100644 --- a/agent/init/hook/hook.go +++ b/agent/init/hook/hook.go @@ -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("Version")) + if err != nil { + global.LOG.Fatalf("load base dir 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() diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index 3ddc6cd86..15f3e9116 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -78,6 +78,15 @@ 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: "Version", Value: global.CONF.System.Version}).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 } diff --git a/agent/init/viper/viper.go b/agent/init/viper/viper.go index 734515308..61401f63e 100644 --- a/agent/init/viper/viper.go +++ b/agent/init/viper/viper.go @@ -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)) diff --git a/agent/router/common.go b/agent/router/common.go index f54a07ddc..f729001cc 100644 --- a/agent/router/common.go +++ b/agent/router/common.go @@ -21,5 +21,6 @@ func commonGroups() []CommonRouter { &RuntimeRouter{}, &ProcessRouter{}, &WebsiteCARouter{}, + &UpgradeRouter{}, } } diff --git a/agent/router/upgrade.go b/agent/router/upgrade.go new file mode 100644 index 000000000..b5b378d36 --- /dev/null +++ b/agent/router/upgrade.go @@ -0,0 +1,17 @@ +package router + +import ( + v2 "github.com/1Panel-dev/1Panel/agent/app/api/v2" + "github.com/gin-gonic/gin" +) + +type UpgradeRouter struct{} + +func (s *UpgradeRouter) InitRouter(Router *gin.RouterGroup) { + upgradeRouter := Router.Group("upgrades") + baseApi := v2.ApiGroupApp.BaseApi + { + upgradeRouter.POST("/upgrade", baseApi.Upgrade) + upgradeRouter.POST("/rollback", baseApi.Rollback) + } +} diff --git a/cmd/server/cmd/restore.go b/cmd/server/cmd/restore.go index b800d3c9b..f4dbf5b8f 100644 --- a/cmd/server/cmd/restore.go +++ b/cmd/server/cmd/restore.go @@ -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 -} diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index fe3e0010c..4f2344988 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -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) - } -} diff --git a/core/constant/status.go b/core/constant/status.go index 166c0483c..9a8d9c731 100644 --- a/core/constant/status.go +++ b/core/constant/status.go @@ -12,4 +12,5 @@ const ( StatusStarting = "starting" StatusHealthy = "healthy" StatusUnhealthy = "unhealthy" + StatusUpgrading = "upgrading" ) diff --git a/core/utils/files/files.go b/core/utils/files/files.go index 6a5c22f42..4ed117c97 100644 --- a/core/utils/files/files.go +++ b/core/utils/files/files.go @@ -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 { diff --git a/core/utils/ssh/ssh.go b/core/utils/ssh/ssh.go index 6f6f12c27..b2a6212f6 100644 --- a/core/utils/ssh/ssh.go +++ b/core/utils/ssh/ssh.go @@ -71,6 +71,17 @@ func (c *SSHClient) Run(shell string) (string, error) { return string(buf), err } +func (c *SSHClient) Runf(shell string, args ...string) (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() } diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index d59e17ba6..e8c7f1732 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -130,11 +130,11 @@ export const loadSnapshotSize = (param: SearchWithPage) => { // upgrade export const loadUpgradeInfo = () => { - return http.get(`/settings/upgrade`); + return http.get(`/core/settings/upgrade`); }; export const loadReleaseNotes = (version: string) => { - return http.post(`/settings/upgrade/notes`, { version: version }); + return http.post(`/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 }); };