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
 | 
					package apinodeutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"compress/gzip"
 | 
						"compress/gzip"
 | 
				
			||||||
	"crypto/md5"
 | 
						"crypto/md5"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
@@ -16,9 +15,7 @@ import (
 | 
				
			|||||||
	stringutil "github.com/iwind/TeaGo/utils/string"
 | 
						stringutil "github.com/iwind/TeaGo/utils/string"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,49 +24,31 @@ type Progress struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Upgrader struct {
 | 
					type Upgrader struct {
 | 
				
			||||||
	progress *Progress
 | 
						progress  *Progress
 | 
				
			||||||
	apiExe   string
 | 
						apiExe    string
 | 
				
			||||||
 | 
						apiNodeId int64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewUpgrader() *Upgrader {
 | 
					func NewUpgrader(apiNodeId int64) *Upgrader {
 | 
				
			||||||
	return &Upgrader{
 | 
						return &Upgrader{
 | 
				
			||||||
		apiExe:   Tea.Root + "/edge-api/bin/edge-api",
 | 
							apiExe:    apiExe(),
 | 
				
			||||||
		progress: &Progress{Percent: 0},
 | 
							progress:  &Progress{Percent: 0},
 | 
				
			||||||
 | 
							apiNodeId: apiNodeId,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *Upgrader) CanUpgrade(apiVersion string) (canUpgrade bool, reason string) {
 | 
					func (this *Upgrader) Upgrade() error {
 | 
				
			||||||
	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 {
 | 
					 | 
				
			||||||
	sharedClient, err := rpc.SharedRPC()
 | 
						sharedClient, err := rpc.SharedRPC()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var apiNode = apiNodeResp.ApiNode
 | 
						var apiNode = apiNodeResp.ApiNode
 | 
				
			||||||
	if apiNode == nil {
 | 
						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()
 | 
						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 {
 | 
						if !canUpgrade {
 | 
				
			||||||
		return errors.New(reason)
 | 
							return errors.New(reason)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	localVersion, err := this.localVersion()
 | 
						localVersion, err := localVersion()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.New("lookup version failed: " + err.Error())
 | 
							return errors.New("lookup version failed: " + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -220,22 +199,3 @@ func (this *Upgrader) Upgrade(apiNodeId int64) error {
 | 
				
			|||||||
func (this *Upgrader) Progress() *Progress {
 | 
					func (this *Upgrader) Progress() *Progress {
 | 
				
			||||||
	return this.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 (
 | 
					import (
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
 | 
				
			||||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
						_ "github.com/iwind/TeaGo/bootstrap"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUpgrader_CanUpgrade(t *testing.T) {
 | 
					func TestUpgrader_CanUpgrade(t *testing.T) {
 | 
				
			||||||
	var upgrader = apinodeutils.NewUpgrader()
 | 
						t.Log(apinodeutils.CanUpgrade("0.6.3", runtime.GOOS, runtime.GOARCH))
 | 
				
			||||||
	t.Log(upgrader.CanUpgrade("0.6.3"))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUpgrader_Upgrade(t *testing.T) {
 | 
					func TestUpgrader_Upgrade(t *testing.T) {
 | 
				
			||||||
	var upgrader = apinodeutils.NewUpgrader()
 | 
						var upgrader = apinodeutils.NewUpgrader(1)
 | 
				
			||||||
	err := upgrader.Upgrade(1)
 | 
						err := upgrader.Upgrade()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							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 (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"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/EdgeAdmin/internal/web/actions/actionutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
				
			||||||
	"github.com/iwind/TeaGo/logs"
 | 
						"github.com/iwind/TeaGo/logs"
 | 
				
			||||||
	"github.com/iwind/TeaGo/maps"
 | 
						"github.com/iwind/TeaGo/maps"
 | 
				
			||||||
 | 
						stringutil "github.com/iwind/TeaGo/utils/string"
 | 
				
			||||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
						timeutil "github.com/iwind/TeaGo/utils/time"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -44,7 +47,7 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		for _, node := range nodesResp.ApiNodes {
 | 
							for _, node := range nodesResp.ApiNodes {
 | 
				
			||||||
			// 状态
 | 
								// 状态
 | 
				
			||||||
			status := &nodeconfigs.NodeStatus{}
 | 
								var status = &nodeconfigs.NodeStatus{}
 | 
				
			||||||
			if len(node.StatusJSON) > 0 {
 | 
								if len(node.StatusJSON) > 0 {
 | 
				
			||||||
				err = json.Unmarshal(node.StatusJSON, &status)
 | 
									err = json.Unmarshal(node.StatusJSON, &status)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
@@ -55,7 +58,7 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Rest地址
 | 
								// Rest地址
 | 
				
			||||||
			restAccessAddrs := []string{}
 | 
								var restAccessAddrs = []string{}
 | 
				
			||||||
			if node.RestIsOn {
 | 
								if node.RestIsOn {
 | 
				
			||||||
				if len(node.RestHTTPJSON) > 0 {
 | 
									if len(node.RestHTTPJSON) > 0 {
 | 
				
			||||||
					httpConfig := &serverconfigs.HTTPProtocolConfig{}
 | 
										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{
 | 
								nodeMaps = append(nodeMaps, maps.Map{
 | 
				
			||||||
				"id":              node.Id,
 | 
									"id":              node.Id,
 | 
				
			||||||
				"isOn":            node.IsOn,
 | 
									"isOn":            node.IsOn,
 | 
				
			||||||
@@ -94,14 +100,17 @@ func (this *IndexAction) RunGet(params struct{}) {
 | 
				
			|||||||
				"restAccessAddrs": restAccessAddrs,
 | 
									"restAccessAddrs": restAccessAddrs,
 | 
				
			||||||
				"isPrimary":       node.IsPrimary,
 | 
									"isPrimary":       node.IsPrimary,
 | 
				
			||||||
				"status": maps.Map{
 | 
									"status": maps.Map{
 | 
				
			||||||
					"isActive":     status.IsActive,
 | 
										"isActive":      status.IsActive,
 | 
				
			||||||
					"updatedAt":    status.UpdatedAt,
 | 
										"updatedAt":     status.UpdatedAt,
 | 
				
			||||||
					"hostname":     status.Hostname,
 | 
										"hostname":      status.Hostname,
 | 
				
			||||||
					"cpuUsage":     status.CPUUsage,
 | 
										"cpuUsage":      status.CPUUsage,
 | 
				
			||||||
					"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
 | 
										"cpuUsageText":  fmt.Sprintf("%.2f%%", status.CPUUsage*100),
 | 
				
			||||||
					"memUsage":     status.MemoryUsage,
 | 
										"memUsage":      status.MemoryUsage,
 | 
				
			||||||
					"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
 | 
										"memUsageText":  fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
 | 
				
			||||||
					"buildVersion": status.BuildVersion,
 | 
										"buildVersion":  status.BuildVersion,
 | 
				
			||||||
 | 
										"latestVersion": teaconst.APINodeVersion,
 | 
				
			||||||
 | 
										"shouldUpgrade": shouldUpgrade,
 | 
				
			||||||
 | 
										"canUpgrade":    shouldUpgrade && canUpgrade,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,10 @@ func init() {
 | 
				
			|||||||
			GetPost("/update", new(UpdateAction)).
 | 
								GetPost("/update", new(UpdateAction)).
 | 
				
			||||||
			Get("/install", new(InstallAction)).
 | 
								Get("/install", new(InstallAction)).
 | 
				
			||||||
			Get("/logs", new(LogsAction)).
 | 
								Get("/logs", new(LogsAction)).
 | 
				
			||||||
 | 
								GetPost("/upgradePopup", new(UpgradePopupAction)).
 | 
				
			||||||
 | 
								Post("/upgradeCheck", new(UpgradeCheckAction)).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								//
 | 
				
			||||||
			EndAll()
 | 
								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>
 | 
					        </thead>
 | 
				
			||||||
        <tr v-for="node in nodes">
 | 
					        <tr v-for="node in nodes">
 | 
				
			||||||
            <td><a :href="'/api/node?nodeId=' + node.id">{{node.name}}</a>
 | 
					            <td><a :href="'/api/node?nodeId=' + node.id">{{node.name}}</a>
 | 
				
			||||||
                <grey-label v-if="node.isPrimary">主节点</grey-label>
 | 
					                <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>
 | 
				
			||||||
            <td>
 | 
					            <td>
 | 
				
			||||||
                <div v-if="node.accessAddrs != null && node.accessAddrs.length > 0">
 | 
					                <div v-if="node.accessAddrs != null && node.accessAddrs.length > 0">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
Tea.context(function () {
 | 
					Tea.context(function () {
 | 
				
			||||||
	// 创建节点
 | 
						// 创建节点
 | 
				
			||||||
	this.createNode = function () {
 | 
						this.createNode = function () {
 | 
				
			||||||
		teaweb.popup("/api/node/createPopup", {
 | 
							teaweb.popup(".node.createPopup", {
 | 
				
			||||||
			width: "50em",
 | 
								width: "50em",
 | 
				
			||||||
			height: "30em",
 | 
								height: "30em",
 | 
				
			||||||
			callback: function () {
 | 
								callback: function () {
 | 
				
			||||||
@@ -16,11 +16,20 @@ Tea.context(function () {
 | 
				
			|||||||
	this.deleteNode = function (nodeId) {
 | 
						this.deleteNode = function (nodeId) {
 | 
				
			||||||
		let that = this
 | 
							let that = this
 | 
				
			||||||
		teaweb.confirm("确定要删除此节点吗?", function () {
 | 
							teaweb.confirm("确定要删除此节点吗?", function () {
 | 
				
			||||||
			that.$post("/api/delete")
 | 
								that.$post(".delete")
 | 
				
			||||||
				.params({
 | 
									.params({
 | 
				
			||||||
					nodeId: nodeId
 | 
										nodeId: nodeId
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
				.refresh()
 | 
									.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