mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-07 23:30: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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,47 +26,29 @@ 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,
|
||||||
@@ -102,6 +108,9 @@ func (this *IndexAction) RunGet(params struct{}) {
|
|||||||
"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>
|
||||||
|
<div v-if="node.isPrimary">
|
||||||
<grey-label v-if="node.isPrimary">主节点</grey-label>
|
<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