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

ssongliu 3 days ago
parent b6641b2776
commit f96e633de8

4
.gitignore vendored

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

@ -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:

@ -64,4 +64,6 @@ var (
websiteCAService = service.NewIWebsiteCAService()
taskService = service.NewITaskService()
upgradeService = service.NewIUpgradeService()
)

@ -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)
}

@ -0,0 +1,10 @@
package dto
type Upgrade struct {
Version string `json:"version"`
UpgradePath string `json:"upgradePath"`
}
type Rollback struct {
Version string `json:"version"`
}

@ -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))

@ -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()

@ -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")
},

@ -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]
}

@ -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()

@ -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
}

@ -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))

@ -21,5 +21,6 @@ func commonGroups() []CommonRouter {
&RuntimeRouter{},
&ProcessRouter{},
&WebsiteCARouter{},
&UpgradeRouter{},
}
}

@ -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)
}
}

@ -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
}

@ -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)
}
}

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

@ -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 {

@ -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()
}

@ -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 });
};

Loading…
Cancel
Save