2023-01-30 21:05:20 +08:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
2023-03-21 15:16:28 +08:00
|
|
|
"encoding/json"
|
2023-01-30 21:05:20 +08:00
|
|
|
"fmt"
|
2023-04-07 11:30:10 +08:00
|
|
|
"io"
|
2023-02-07 15:33:47 +08:00
|
|
|
"net/http"
|
2023-01-30 21:05:20 +08:00
|
|
|
"os"
|
2023-08-01 22:05:14 +08:00
|
|
|
"path"
|
2023-02-05 23:48:37 +08:00
|
|
|
"strings"
|
2023-01-30 21:05:20 +08:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
2023-06-07 14:53:24 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
2023-01-30 21:05:20 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/global"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
2023-03-17 18:05:21 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
2023-01-30 21:05:20 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
|
|
|
)
|
|
|
|
|
|
|
|
type UpgradeService struct{}
|
|
|
|
|
|
|
|
type IUpgradeService interface {
|
2023-02-05 23:48:37 +08:00
|
|
|
Upgrade(req dto.Upgrade) error
|
2023-03-21 15:16:28 +08:00
|
|
|
LoadNotes(req dto.Upgrade) (string, error)
|
2023-01-30 21:05:20 +08:00
|
|
|
SearchUpgrade() (*dto.UpgradeInfo, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewIUpgradeService() IUpgradeService {
|
|
|
|
return &UpgradeService{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
2023-02-17 16:22:59 +08:00
|
|
|
var upgrade dto.UpgradeInfo
|
2023-02-10 18:07:29 +08:00
|
|
|
currentVersion, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
|
2023-01-30 21:05:20 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-02-07 15:33:47 +08:00
|
|
|
|
2023-03-21 15:16:28 +08:00
|
|
|
latestVersion, err := u.loadVersion(true, currentVersion.Value)
|
2023-02-17 16:22:59 +08:00
|
|
|
if err != nil {
|
2023-03-21 15:16:28 +08:00
|
|
|
global.LOG.Infof("load latest version failed, err: %v", err)
|
2023-02-17 16:22:59 +08:00
|
|
|
return nil, err
|
2023-01-30 21:05:20 +08:00
|
|
|
}
|
2024-04-12 11:04:06 +08:00
|
|
|
if !common.ComparePanelVersion(string(latestVersion), currentVersion.Value) {
|
2023-02-17 16:22:59 +08:00
|
|
|
return nil, err
|
2023-01-30 21:05:20 +08:00
|
|
|
}
|
2023-03-21 15:16:28 +08:00
|
|
|
upgrade.LatestVersion = latestVersion
|
|
|
|
if latestVersion[0:4] == currentVersion.Value[0:4] {
|
|
|
|
upgrade.NewVersion = ""
|
|
|
|
} else {
|
|
|
|
newerVersion, err := u.loadVersion(false, currentVersion.Value)
|
|
|
|
if err != nil {
|
|
|
|
global.LOG.Infof("load newer version failed, err: %v", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if newerVersion == currentVersion.Value {
|
|
|
|
upgrade.NewVersion = ""
|
|
|
|
} else {
|
|
|
|
upgrade.NewVersion = newerVersion
|
|
|
|
}
|
2023-01-30 21:05:20 +08:00
|
|
|
}
|
2023-03-24 11:52:10 +08:00
|
|
|
itemVersion := upgrade.LatestVersion
|
|
|
|
if upgrade.NewVersion != "" {
|
|
|
|
itemVersion = upgrade.NewVersion
|
|
|
|
}
|
|
|
|
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion))
|
|
|
|
|
2023-02-17 16:22:59 +08:00
|
|
|
if err != nil {
|
2023-05-30 15:30:57 +08:00
|
|
|
return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", latestVersion, err)
|
2023-02-17 16:22:59 +08:00
|
|
|
}
|
2023-03-21 15:16:28 +08:00
|
|
|
upgrade.ReleaseNote = notes
|
|
|
|
return &upgrade, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
|
|
|
|
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version))
|
2023-02-17 16:22:59 +08:00
|
|
|
if err != nil {
|
2023-05-30 15:30:57 +08:00
|
|
|
return "", fmt.Errorf("load releases-notes of version %s failed, err: %v", req.Version, err)
|
2023-02-17 16:22:59 +08:00
|
|
|
}
|
2023-03-21 15:16:28 +08:00
|
|
|
return notes, nil
|
2023-01-30 21:05:20 +08:00
|
|
|
}
|
|
|
|
|
2023-02-05 23:48:37 +08:00
|
|
|
func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
2023-01-30 21:05:20 +08:00
|
|
|
global.LOG.Info("start to upgrade now...")
|
|
|
|
fileOp := files.NewFileOp()
|
|
|
|
timeStr := time.Now().Format("20060102150405")
|
2023-08-01 22:05:14 +08:00
|
|
|
rootDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("upgrade/upgrade_%s/downloads", timeStr))
|
|
|
|
originalDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("upgrade/upgrade_%s/original", timeStr))
|
2023-02-05 23:48:37 +08:00
|
|
|
if err := os.MkdirAll(rootDir, os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := os.MkdirAll(originalDir, os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-06-02 17:03:20 +08:00
|
|
|
itemArch, err := loadArch()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-01-30 21:05:20 +08:00
|
|
|
|
2023-02-17 16:22:59 +08:00
|
|
|
downloadPath := fmt.Sprintf("%s/%s/%s/release", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version)
|
2023-06-02 17:03:20 +08:00
|
|
|
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
|
2023-02-02 15:01:37 +08:00
|
|
|
_ = settingRepo.Update("SystemStatus", "Upgrading")
|
2023-01-30 21:05:20 +08:00
|
|
|
go func() {
|
2023-09-13 15:54:12 +08:00
|
|
|
_ = global.Cron.Stop()
|
|
|
|
defer func() {
|
|
|
|
global.Cron.Start()
|
|
|
|
}()
|
2023-03-14 16:45:32 +08:00
|
|
|
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
|
2023-02-05 23:48:37 +08:00
|
|
|
global.LOG.Errorf("download service file failed, err: %v", err)
|
2023-02-09 16:36:28 +08:00
|
|
|
_ = settingRepo.Update("SystemStatus", "Free")
|
2023-01-30 21:05:20 +08:00
|
|
|
return
|
|
|
|
}
|
2023-02-05 23:48:37 +08:00
|
|
|
global.LOG.Info("download all file successful!")
|
2023-01-30 21:05:20 +08:00
|
|
|
defer func() {
|
2023-02-05 23:48:37 +08:00
|
|
|
_ = os.Remove(rootDir)
|
2023-01-30 21:05:20 +08:00
|
|
|
}()
|
2023-03-14 16:45:32 +08:00
|
|
|
if err := handleUnTar(rootDir+"/"+fileName, rootDir); err != nil {
|
2023-02-02 15:01:37 +08:00
|
|
|
global.LOG.Errorf("decompress file failed, err: %v", err)
|
2023-02-09 16:36:28 +08:00
|
|
|
_ = settingRepo.Update("SystemStatus", "Free")
|
2023-01-30 21:05:20 +08:00
|
|
|
return
|
|
|
|
}
|
2023-03-14 16:45:32 +08:00
|
|
|
tmpDir := rootDir + "/" + strings.ReplaceAll(fileName, ".tar.gz", "")
|
2023-01-30 21:05:20 +08:00
|
|
|
|
|
|
|
if err := u.handleBackup(fileOp, originalDir); err != nil {
|
2023-02-02 15:01:37 +08:00
|
|
|
global.LOG.Errorf("handle backup original file failed, err: %v", err)
|
2023-02-09 16:36:28 +08:00
|
|
|
_ = settingRepo.Update("SystemStatus", "Free")
|
2023-01-30 21:05:20 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
global.LOG.Info("backup original data successful, now start to upgrade!")
|
|
|
|
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(tmpDir, "1panel"), "/usr/local/bin"); err != nil {
|
2023-02-02 15:01:37 +08:00
|
|
|
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
|
2024-03-01 16:55:02 +08:00
|
|
|
u.handleRollback(originalDir, 1)
|
2023-01-30 21:05:20 +08:00
|
|
|
return
|
|
|
|
}
|
2023-03-14 21:55:28 +08:00
|
|
|
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(tmpDir, "1pctl"), "/usr/local/bin"); err != nil {
|
2023-02-02 15:01:37 +08:00
|
|
|
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
|
2024-03-01 16:55:02 +08:00
|
|
|
u.handleRollback(originalDir, 2)
|
2023-01-30 21:05:20 +08:00
|
|
|
return
|
|
|
|
}
|
2024-04-07 23:21:15 +08:00
|
|
|
if _, err := cmd.Execf("sed -i -e 's#BASE_DIR=.*#BASE_DIR=%s#g' /usr/local/bin/1pctl", global.CONF.System.BaseDir); err != nil {
|
2023-03-14 21:55:28 +08:00
|
|
|
global.LOG.Errorf("upgrade basedir in 1pctl failed, err: %v", err)
|
2024-03-01 16:55:02 +08:00
|
|
|
u.handleRollback(originalDir, 2)
|
2023-03-14 21:55:28 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(tmpDir, "1panel.service"), "/etc/systemd/system"); err != nil {
|
2023-02-02 15:01:37 +08:00
|
|
|
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
|
2024-03-01 16:55:02 +08:00
|
|
|
u.handleRollback(originalDir, 3)
|
2023-01-30 21:05:20 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
global.LOG.Info("upgrade successful!")
|
2023-03-15 15:58:26 +08:00
|
|
|
go writeLogs(req.Version)
|
2023-02-05 23:48:37 +08:00
|
|
|
_ = settingRepo.Update("SystemVersion", req.Version)
|
|
|
|
_ = settingRepo.Update("SystemStatus", "Free")
|
2024-01-02 17:10:13 +08:00
|
|
|
checkPointOfWal()
|
2023-04-17 14:03:01 +08:00
|
|
|
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute)
|
2023-01-30 21:05:20 +08:00
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UpgradeService) handleBackup(fileOp files.FileOp, originalDir string) error {
|
|
|
|
if err := fileOp.Copy("/usr/local/bin/1panel", originalDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := fileOp.Copy("/usr/local/bin/1pctl", originalDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := fileOp.Copy("/etc/systemd/system/1panel.service", originalDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dbPath := global.CONF.System.DbPath + "/" + global.CONF.System.DbFile
|
|
|
|
if err := fileOp.Copy(dbPath, originalDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-01 16:55:02 +08:00
|
|
|
func (u *UpgradeService) handleRollback(originalDir string, errStep int) {
|
2023-02-09 16:36:28 +08:00
|
|
|
_ = settingRepo.Update("SystemStatus", "Free")
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(originalDir, "1Panel.db"), global.CONF.System.DbPath); err != nil {
|
2023-01-30 21:05:20 +08:00
|
|
|
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
|
|
|
}
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(originalDir, "1panel"), "/usr/local/bin"); err != nil {
|
2023-01-30 21:05:20 +08:00
|
|
|
global.LOG.Errorf("rollback 1pctl failed, err: %v", err)
|
|
|
|
}
|
|
|
|
if errStep == 1 {
|
|
|
|
return
|
|
|
|
}
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(originalDir, "1pctl"), "/usr/local/bin"); err != nil {
|
2023-01-30 21:05:20 +08:00
|
|
|
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
|
|
|
}
|
|
|
|
if errStep == 2 {
|
|
|
|
return
|
|
|
|
}
|
2024-04-06 22:32:10 +08:00
|
|
|
if err := common.CopyFile(path.Join(originalDir, "1panel.service"), "/etc/systemd/system"); err != nil {
|
2023-01-30 21:05:20 +08:00
|
|
|
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
|
|
|
}
|
2023-03-21 15:16:28 +08:00
|
|
|
}
|
2023-02-09 16:36:28 +08:00
|
|
|
|
2023-03-21 15:16:28 +08:00
|
|
|
func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (string, error) {
|
|
|
|
path := fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, global.CONF.System.Mode)
|
|
|
|
if !isLatest {
|
|
|
|
path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, global.CONF.System.Mode)
|
|
|
|
}
|
|
|
|
latestVersionRes, err := http.Get(path)
|
|
|
|
if err != nil {
|
2023-06-07 14:53:24 +08:00
|
|
|
return "", buserr.New(constant.ErrOSSConn)
|
2023-03-21 15:16:28 +08:00
|
|
|
}
|
|
|
|
defer latestVersionRes.Body.Close()
|
2023-04-07 11:30:10 +08:00
|
|
|
version, err := io.ReadAll(latestVersionRes.Body)
|
2023-03-21 15:16:28 +08:00
|
|
|
if err != nil {
|
2023-06-07 14:53:24 +08:00
|
|
|
return "", buserr.New(constant.ErrOSSConn)
|
2023-03-21 15:16:28 +08:00
|
|
|
}
|
|
|
|
if isLatest {
|
|
|
|
return string(version), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
versionMap := make(map[string]string)
|
|
|
|
if err := json.Unmarshal(version, &versionMap); err != nil {
|
2023-06-07 14:53:24 +08:00
|
|
|
return "", buserr.New(constant.ErrOSSConn)
|
2023-03-21 15:16:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(currentVersion) < 4 {
|
|
|
|
return "", fmt.Errorf("current version is error format: %s", currentVersion)
|
|
|
|
}
|
|
|
|
if version, ok := versionMap[currentVersion[0:4]]; ok {
|
|
|
|
return version, nil
|
|
|
|
}
|
2023-06-07 14:53:24 +08:00
|
|
|
return "", buserr.New(constant.ErrOSSConn)
|
2023-03-21 15:16:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
|
|
|
releaseNotes, err := http.Get(path)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer releaseNotes.Body.Close()
|
2023-04-07 11:30:10 +08:00
|
|
|
release, err := io.ReadAll(releaseNotes.Body)
|
2023-03-21 15:16:28 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(release), nil
|
2023-01-30 21:05:20 +08:00
|
|
|
}
|
2023-06-02 17:03:20 +08:00
|
|
|
|
|
|
|
func loadArch() (string, error) {
|
2024-03-07 11:05:05 +08:00
|
|
|
std, err := cmd.Exec("uname -a")
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
|
|
|
|
}
|
|
|
|
if strings.Contains(std, "x86_64") {
|
|
|
|
return "amd64", nil
|
|
|
|
}
|
|
|
|
if strings.Contains(std, "arm64") || strings.Contains(std, "aarch64") {
|
|
|
|
return "arm64", nil
|
|
|
|
}
|
|
|
|
if strings.Contains(std, "armv7l") {
|
|
|
|
return "armv7", nil
|
|
|
|
}
|
|
|
|
if strings.Contains(std, "ppc64le") {
|
|
|
|
return "ppc64le", nil
|
|
|
|
}
|
|
|
|
if strings.Contains(std, "s390x") {
|
|
|
|
return "s390x", nil
|
2023-06-02 17:03:20 +08:00
|
|
|
}
|
2024-03-07 11:05:05 +08:00
|
|
|
return "", fmt.Errorf("unsupported such arch: %s", std)
|
2023-06-02 17:03:20 +08:00
|
|
|
}
|