mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 13:10:26 +08:00 
			
		
		
		
	实现API节点远程升级
This commit is contained in:
		
							
								
								
									
										30
									
								
								internal/utils/apinodeutils/manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/utils/apinodeutils/manager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package apinodeutils
 | 
			
		||||
 | 
			
		||||
var SharedManager = NewManager()
 | 
			
		||||
 | 
			
		||||
type Manager struct {
 | 
			
		||||
	upgraderMap map[int64]*Upgrader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewManager() *Manager {
 | 
			
		||||
	return &Manager{
 | 
			
		||||
		upgraderMap: map[int64]*Upgrader{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Manager) AddUpgrader(upgrader *Upgrader) {
 | 
			
		||||
	this.upgraderMap[upgrader.apiNodeId] = upgrader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Manager) FindUpgrader(apiNodeId int64) *Upgrader {
 | 
			
		||||
	return this.upgraderMap[apiNodeId]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Manager) RemoveUpgrader(upgrader *Upgrader) {
 | 
			
		||||
	if upgrader == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	delete(this.upgraderMap, upgrader.apiNodeId)
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
package apinodeutils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"errors"
 | 
			
		||||
@@ -16,9 +15,7 @@ import (
 | 
			
		||||
	stringutil "github.com/iwind/TeaGo/utils/string"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -29,47 +26,29 @@ type Progress struct {
 | 
			
		||||
type Upgrader struct {
 | 
			
		||||
	progress  *Progress
 | 
			
		||||
	apiExe    string
 | 
			
		||||
	apiNodeId int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUpgrader() *Upgrader {
 | 
			
		||||
func NewUpgrader(apiNodeId int64) *Upgrader {
 | 
			
		||||
	return &Upgrader{
 | 
			
		||||
		apiExe:   Tea.Root + "/edge-api/bin/edge-api",
 | 
			
		||||
		apiExe:    apiExe(),
 | 
			
		||||
		progress:  &Progress{Percent: 0},
 | 
			
		||||
		apiNodeId: apiNodeId,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Upgrader) CanUpgrade(apiVersion string) (canUpgrade bool, reason string) {
 | 
			
		||||
	stat, err := os.Stat(this.apiExe)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, "stat error: " + err.Error()
 | 
			
		||||
	}
 | 
			
		||||
	if stat.IsDir() {
 | 
			
		||||
		return false, "is directory"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localVersion, err := this.localVersion()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, "lookup version failed: " + err.Error()
 | 
			
		||||
	}
 | 
			
		||||
	if stringutil.VersionCompare(localVersion, apiVersion) <= 0 {
 | 
			
		||||
		return false, "need not upgrade, local '" + localVersion + "' vs remote '" + apiVersion + "'"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Upgrader) Upgrade(apiNodeId int64) error {
 | 
			
		||||
func (this *Upgrader) Upgrade() error {
 | 
			
		||||
	sharedClient, err := rpc.SharedRPC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	apiNodeResp, err := sharedClient.APINodeRPC().FindEnabledAPINode(sharedClient.Context(0), &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
 | 
			
		||||
	apiNodeResp, err := sharedClient.APINodeRPC().FindEnabledAPINode(sharedClient.Context(0), &pb.FindEnabledAPINodeRequest{ApiNodeId: this.apiNodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	var apiNode = apiNodeResp.ApiNode
 | 
			
		||||
	if apiNode == nil {
 | 
			
		||||
		return errors.New("could not find api node with id '" + types.String(apiNodeId) + "'")
 | 
			
		||||
		return errors.New("could not find api node with id '" + types.String(this.apiNodeId) + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	apiConfig, err := configs.LoadAPIConfig()
 | 
			
		||||
@@ -93,12 +72,12 @@ func (this *Upgrader) Upgrade(apiNodeId int64) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查本地文件版本
 | 
			
		||||
	canUpgrade, reason := this.CanUpgrade(versionResp.Version)
 | 
			
		||||
	canUpgrade, reason := CanUpgrade(versionResp.Version, versionResp.Os, versionResp.Arch)
 | 
			
		||||
	if !canUpgrade {
 | 
			
		||||
		return errors.New(reason)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localVersion, err := this.localVersion()
 | 
			
		||||
	localVersion, err := localVersion()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("lookup version failed: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
@@ -220,22 +199,3 @@ func (this *Upgrader) Upgrade(apiNodeId int64) error {
 | 
			
		||||
func (this *Upgrader) Progress() *Progress {
 | 
			
		||||
	return this.progress
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Upgrader) localVersion() (string, error) {
 | 
			
		||||
	var cmd = exec.Command(this.apiExe, "-V")
 | 
			
		||||
	var output = &bytes.Buffer{}
 | 
			
		||||
	cmd.Stdout = output
 | 
			
		||||
	err := cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	var localVersion = output.String()
 | 
			
		||||
 | 
			
		||||
	// 检查版本号
 | 
			
		||||
	var reg = regexp.MustCompile(`^[\d.]+$`)
 | 
			
		||||
	if !reg.MatchString(localVersion) {
 | 
			
		||||
		return "", errors.New("lookup version failed: " + localVersion)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return localVersion, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,17 +5,17 @@ package apinodeutils_test
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestUpgrader_CanUpgrade(t *testing.T) {
 | 
			
		||||
	var upgrader = apinodeutils.NewUpgrader()
 | 
			
		||||
	t.Log(upgrader.CanUpgrade("0.6.3"))
 | 
			
		||||
	t.Log(apinodeutils.CanUpgrade("0.6.3", runtime.GOOS, runtime.GOARCH))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpgrader_Upgrade(t *testing.T) {
 | 
			
		||||
	var upgrader = apinodeutils.NewUpgrader()
 | 
			
		||||
	err := upgrader.Upgrade(1)
 | 
			
		||||
	var upgrader = apinodeutils.NewUpgrader(1)
 | 
			
		||||
	err := upgrader.Upgrade()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										76
									
								
								internal/utils/apinodeutils/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								internal/utils/apinodeutils/utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package apinodeutils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
	stringutil "github.com/iwind/TeaGo/utils/string"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool, reason string) {
 | 
			
		||||
	if len(apiVersion) == 0 {
 | 
			
		||||
		return false, "current api version should not be empty"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if osName != runtime.GOOS {
 | 
			
		||||
		return false, "os not match: " + osName
 | 
			
		||||
	}
 | 
			
		||||
	if arch != runtime.GOARCH {
 | 
			
		||||
		return false, "arch not match: " + arch
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stat, err := os.Stat(apiExe())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, "stat error: " + err.Error()
 | 
			
		||||
	}
 | 
			
		||||
	if stat.IsDir() {
 | 
			
		||||
		return false, "is directory"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localVersion, err := localVersion()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, "lookup version failed: " + err.Error()
 | 
			
		||||
	}
 | 
			
		||||
	if localVersion != teaconst.APINodeVersion {
 | 
			
		||||
		return false, "not newest api node"
 | 
			
		||||
	}
 | 
			
		||||
	if stringutil.VersionCompare(localVersion, apiVersion) <= 0 {
 | 
			
		||||
		return false, "need not upgrade, local '" + localVersion + "' vs remote '" + apiVersion + "'"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func localVersion() (string, error) {
 | 
			
		||||
	var cmd = exec.Command(apiExe(), "-V")
 | 
			
		||||
	var output = &bytes.Buffer{}
 | 
			
		||||
	cmd.Stdout = output
 | 
			
		||||
	err := cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	var localVersion = strings.TrimSpace(output.String())
 | 
			
		||||
 | 
			
		||||
	// 检查版本号
 | 
			
		||||
	var reg = regexp.MustCompile(`^[\d.]+$`)
 | 
			
		||||
	if !reg.MatchString(localVersion) {
 | 
			
		||||
		return "", errors.New("lookup version failed: " + localVersion)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return localVersion, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func apiExe() string {
 | 
			
		||||
	return Tea.Root + "/edge-api/bin/edge-api"
 | 
			
		||||
}
 | 
			
		||||
@@ -3,12 +3,15 @@ package api
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	stringutil "github.com/iwind/TeaGo/utils/string"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -44,7 +47,7 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
			
		||||
 | 
			
		||||
		for _, node := range nodesResp.ApiNodes {
 | 
			
		||||
			// 状态
 | 
			
		||||
			status := &nodeconfigs.NodeStatus{}
 | 
			
		||||
			var status = &nodeconfigs.NodeStatus{}
 | 
			
		||||
			if len(node.StatusJSON) > 0 {
 | 
			
		||||
				err = json.Unmarshal(node.StatusJSON, &status)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -55,7 +58,7 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Rest地址
 | 
			
		||||
			restAccessAddrs := []string{}
 | 
			
		||||
			var restAccessAddrs = []string{}
 | 
			
		||||
			if node.RestIsOn {
 | 
			
		||||
				if len(node.RestHTTPJSON) > 0 {
 | 
			
		||||
					httpConfig := &serverconfigs.HTTPProtocolConfig{}
 | 
			
		||||
@@ -86,6 +89,9 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var shouldUpgrade = status.IsActive && len(status.BuildVersion) > 0 && stringutil.VersionCompare(teaconst.APINodeVersion, status.BuildVersion) > 0
 | 
			
		||||
			canUpgrade, _ := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
 | 
			
		||||
 | 
			
		||||
			nodeMaps = append(nodeMaps, maps.Map{
 | 
			
		||||
				"id":              node.Id,
 | 
			
		||||
				"isOn":            node.IsOn,
 | 
			
		||||
@@ -102,6 +108,9 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
			
		||||
					"memUsage":      status.MemoryUsage,
 | 
			
		||||
					"memUsageText":  fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
 | 
			
		||||
					"buildVersion":  status.BuildVersion,
 | 
			
		||||
					"latestVersion": teaconst.APINodeVersion,
 | 
			
		||||
					"shouldUpgrade": shouldUpgrade,
 | 
			
		||||
					"canUpgrade":    shouldUpgrade && canUpgrade,
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,10 @@ func init() {
 | 
			
		||||
			GetPost("/update", new(UpdateAction)).
 | 
			
		||||
			Get("/install", new(InstallAction)).
 | 
			
		||||
			Get("/logs", new(LogsAction)).
 | 
			
		||||
			GetPost("/upgradePopup", new(UpgradePopupAction)).
 | 
			
		||||
			Post("/upgradeCheck", new(UpgradeCheckAction)).
 | 
			
		||||
 | 
			
		||||
			//
 | 
			
		||||
			EndAll()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								internal/web/actions/default/api/node/upgradeCheck.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								internal/web/actions/default/api/node/upgradeCheck.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package node
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/configs"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// UpgradeCheckAction 检查升级结果
 | 
			
		||||
type UpgradeCheckAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpgradeCheckAction) Init() {
 | 
			
		||||
	this.Nav("", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpgradeCheckAction) RunPost(params struct {
 | 
			
		||||
	NodeId int64
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["isOk"] = false
 | 
			
		||||
 | 
			
		||||
	nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Success()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var node = nodeResp.ApiNode
 | 
			
		||||
	if node == nil || len(node.AccessAddrs) == 0 {
 | 
			
		||||
		this.Success()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	apiConfig, err := configs.LoadAPIConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Success()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var newAPIConfig = apiConfig.Clone()
 | 
			
		||||
	newAPIConfig.RPC.Endpoints = node.AccessAddrs
 | 
			
		||||
	rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Success()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(rpcClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Success()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if versionResp.Version != teaconst.Version {
 | 
			
		||||
		this.Success()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["isOk"] = true
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										124
									
								
								internal/web/actions/default/api/node/upgradePopup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								internal/web/actions/default/api/node/upgradePopup.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package node
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/actions"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UpgradePopupAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpgradePopupAction) Init() {
 | 
			
		||||
	this.Nav("", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpgradePopupAction) RunGet(params struct {
 | 
			
		||||
	NodeId int64
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["nodeId"] = params.NodeId
 | 
			
		||||
	this.Data["nodeName"] = ""
 | 
			
		||||
	this.Data["currentVersion"] = ""
 | 
			
		||||
	this.Data["latestVersion"] = ""
 | 
			
		||||
	this.Data["result"] = ""
 | 
			
		||||
	this.Data["resultIsOk"] = true
 | 
			
		||||
	this.Data["canUpgrade"] = false
 | 
			
		||||
	this.Data["isUpgrading"] = false
 | 
			
		||||
 | 
			
		||||
	nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var node = nodeResp.ApiNode
 | 
			
		||||
	if node == nil {
 | 
			
		||||
		this.Data["result"] = "要升级的节点不存在"
 | 
			
		||||
		this.Data["resultIsOk"] = false
 | 
			
		||||
		this.Show()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["nodeName"] = node.Name + " / [" + strings.Join(node.AccessAddrs, ", ") + "]"
 | 
			
		||||
 | 
			
		||||
	// 节点状态
 | 
			
		||||
	var status = &nodeconfigs.NodeStatus{}
 | 
			
		||||
	if len(node.StatusJSON) > 0 {
 | 
			
		||||
		err = json.Unmarshal(node.StatusJSON, &status)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(errors.New("decode status failed: " + err.Error()))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["currentVersion"] = status.BuildVersion
 | 
			
		||||
	} else {
 | 
			
		||||
		this.Data["result"] = "无法检测到节点当前版本"
 | 
			
		||||
		this.Data["resultIsOk"] = false
 | 
			
		||||
		this.Show()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["latestVersion"] = teaconst.APINodeVersion
 | 
			
		||||
 | 
			
		||||
	if status.IsActive && len(status.BuildVersion) > 0 {
 | 
			
		||||
		canUpgrade, reason := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
 | 
			
		||||
		if !canUpgrade {
 | 
			
		||||
			this.Data["result"] = reason
 | 
			
		||||
			this.Data["resultIsOk"] = false
 | 
			
		||||
			this.Show()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["canUpgrade"] = true
 | 
			
		||||
		this.Data["result"] = "等待升级"
 | 
			
		||||
		this.Data["resultIsOk"] = true
 | 
			
		||||
	} else {
 | 
			
		||||
		this.Data["result"] = "当前节点非连接状态无法远程升级"
 | 
			
		||||
		this.Data["resultIsOk"] = false
 | 
			
		||||
		this.Show()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 是否正在升级
 | 
			
		||||
	var oldUpgrader = apinodeutils.SharedManager.FindUpgrader(params.NodeId)
 | 
			
		||||
	if oldUpgrader != nil {
 | 
			
		||||
		this.Data["result"] = "正在升级中..."
 | 
			
		||||
		this.Data["resultIsOk"] = false
 | 
			
		||||
		this.Data["isUpgrading"] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpgradePopupAction) RunPost(params struct {
 | 
			
		||||
	NodeId int64
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
}) {
 | 
			
		||||
	var manager = apinodeutils.SharedManager
 | 
			
		||||
 | 
			
		||||
	var oldUpgrader = manager.FindUpgrader(params.NodeId)
 | 
			
		||||
	if oldUpgrader != nil {
 | 
			
		||||
		this.Fail("正在升级中,无需重复提交 ...")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var upgrader = apinodeutils.NewUpgrader(params.NodeId)
 | 
			
		||||
	manager.AddUpgrader(upgrader)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		manager.RemoveUpgrader(upgrader)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	err := upgrader.Upgrade()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Fail("升级失败:" + err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,12 @@
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tr v-for="node in nodes">
 | 
			
		||||
            <td><a :href="'/api/node?nodeId=' + node.id">{{node.name}}</a>
 | 
			
		||||
                <div v-if="node.isPrimary">
 | 
			
		||||
                    <grey-label v-if="node.isPrimary">主节点</grey-label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div v-if="node.status != null && node.status.shouldUpgrade">
 | 
			
		||||
                    <span class="red small">v{{node.status.buildVersion}} -> v{{node.status.latestVersion}}<br/><a href="" v-if="node.status.canUpgrade" @click.prevent="upgradeNode(node.id)">[远程升级]</a> </span>
 | 
			
		||||
                </div>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div v-if="node.accessAddrs != null && node.accessAddrs.length > 0">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	// 创建节点
 | 
			
		||||
	this.createNode = function () {
 | 
			
		||||
		teaweb.popup("/api/node/createPopup", {
 | 
			
		||||
		teaweb.popup(".node.createPopup", {
 | 
			
		||||
			width: "50em",
 | 
			
		||||
			height: "30em",
 | 
			
		||||
			callback: function () {
 | 
			
		||||
@@ -16,11 +16,20 @@ Tea.context(function () {
 | 
			
		||||
	this.deleteNode = function (nodeId) {
 | 
			
		||||
		let that = this
 | 
			
		||||
		teaweb.confirm("确定要删除此节点吗?", function () {
 | 
			
		||||
			that.$post("/api/delete")
 | 
			
		||||
			that.$post(".delete")
 | 
			
		||||
				.params({
 | 
			
		||||
					nodeId: nodeId
 | 
			
		||||
				})
 | 
			
		||||
				.refresh()
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 升级节点
 | 
			
		||||
	this.upgradeNode = function (nodeId) {
 | 
			
		||||
		teaweb.popup(".node.upgradePopup?nodeId=" + nodeId, {
 | 
			
		||||
			onClose: function () {
 | 
			
		||||
				teaweb.reload()
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										35
									
								
								web/views/@default/api/node/upgradePopup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								web/views/@default/api/node/upgradePopup.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
{$layout "layout_popup"}
 | 
			
		||||
 | 
			
		||||
<h3>远程升级</h3>
 | 
			
		||||
 | 
			
		||||
<form class="ui form" data-tea-action="$" data-tea-success="success" data-tea-timeout="3600" data-tea-before="before" data-tea-done="done">
 | 
			
		||||
    <csrf-token></csrf-token>
 | 
			
		||||
    <input type="hidden" name="nodeId" :value="nodeId"/>
 | 
			
		||||
 | 
			
		||||
    <table class="ui table definition selectable">
 | 
			
		||||
        <tr v-show="nodeName.length > 0">
 | 
			
		||||
            <td class="title">API节点</td>
 | 
			
		||||
            <td>{{nodeName}}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr v-show="currentVersion.length > 0">
 | 
			
		||||
            <td>当前版本</td>
 | 
			
		||||
            <td>v{{currentVersion}}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr v-show="latestVersion.length > 0">
 | 
			
		||||
            <td>目标版本</td>
 | 
			
		||||
            <td>v{{latestVersion}}</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">升级结果</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="currentVersion == latestVersion" class="green">已经升级到最新版本</span>
 | 
			
		||||
                <span :class="{red: !resultIsOk}" v-if="currentVersion != latestVersion">
 | 
			
		||||
                    <span v-if="!isRequesting">{{result}}</span>
 | 
			
		||||
                    <span v-if="isRequesting">升级中...</span>
 | 
			
		||||
                </span>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
    <submit-btn v-show="canUpgrade && !isRequesting && !isUpgrading">开始升级</submit-btn>
 | 
			
		||||
</form>
 | 
			
		||||
							
								
								
									
										39
									
								
								web/views/@default/api/node/upgradePopup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								web/views/@default/api/node/upgradePopup.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.$delay(function () {
 | 
			
		||||
		this.checkLoop()
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	this.success = function () {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.isRequesting = false
 | 
			
		||||
 | 
			
		||||
	this.before = function () {
 | 
			
		||||
		this.isRequesting = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.done = function () {
 | 
			
		||||
		this.isRequesting = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.checkLoop = function () {
 | 
			
		||||
		if (this.currentVersion == this.latestVersion) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.$post(".upgradeCheck")
 | 
			
		||||
			.params({
 | 
			
		||||
				nodeId: this.nodeId
 | 
			
		||||
			})
 | 
			
		||||
			.success(function (resp) {
 | 
			
		||||
				if (resp.data.isOk) {
 | 
			
		||||
					teaweb.reload()
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			.done(function () {
 | 
			
		||||
				this.$delay(function () {
 | 
			
		||||
					this.checkLoop()
 | 
			
		||||
				}, 3000)
 | 
			
		||||
			})
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
		Reference in New Issue
	
	Block a user