mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-03 20:34:01 +08:00
增加本地API节点需要升级提示
This commit is contained in:
169
internal/web/actions/default/dashboard/dashboardutils/utils.go
Normal file
169
internal/web/actions/default/dashboard/dashboardutils/utils.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dashboardutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// CheckDiskPartitions 检查服务器磁盘空间
|
||||
func CheckDiskPartitions(thresholdPercent float64) (path string, usage uint64, usagePercent float64, shouldWarning bool) {
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
|
||||
return
|
||||
}
|
||||
|
||||
var rootFS = ""
|
||||
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == "/" {
|
||||
rootFS = p.Fstype
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == "/boot" {
|
||||
continue
|
||||
}
|
||||
if p.Fstype != rootFS {
|
||||
continue
|
||||
}
|
||||
stat, _ := disk.Usage(p.Mountpoint)
|
||||
if stat != nil {
|
||||
if stat.Used < 2*uint64(sizes.G) {
|
||||
continue
|
||||
}
|
||||
if stat.UsedPercent > thresholdPercent {
|
||||
path = stat.Path
|
||||
usage = stat.Used
|
||||
usagePercent = stat.UsedPercent
|
||||
shouldWarning = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CheckLocalAPINode 检查本地的API节点
|
||||
func CheckLocalAPINode(rpcClient *rpc.RPCClient, ctx context.Context) (exePath string, runtimeVersion string, fileVersion string, ok bool) {
|
||||
resp, err := rpcClient.APINodeRPC().FindCurrentAPINode(ctx, &pb.FindCurrentAPINodeRequest{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if resp.ApiNode == nil {
|
||||
return
|
||||
}
|
||||
var instanceCode = resp.ApiNode.InstanceCode
|
||||
if len(instanceCode) == 0 {
|
||||
return
|
||||
}
|
||||
var statusJSON = resp.ApiNode.StatusJSON
|
||||
if len(statusJSON) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
err = json.Unmarshal(statusJSON, status)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
runtimeVersion = status.BuildVersion
|
||||
|
||||
if len(runtimeVersion) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if stringutil.VersionCompare(runtimeVersion, teaconst.APINodeVersion) >= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
exePath = status.ExePath
|
||||
if len(exePath) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
stat, err := os.Stat(exePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return
|
||||
}
|
||||
|
||||
// 实例信息
|
||||
{
|
||||
var outputBuffer = &bytes.Buffer{}
|
||||
var cmd = exec.Command(exePath, "instance")
|
||||
cmd.Stdout = outputBuffer
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var outputBytes = outputBuffer.Bytes()
|
||||
if len(outputBytes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var instanceMap = maps.Map{}
|
||||
err = json.Unmarshal(bytes.TrimSpace(outputBytes), &instanceMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if instanceMap.GetString("code") != instanceCode {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 文件版本
|
||||
{
|
||||
var outputBuffer = &bytes.Buffer{}
|
||||
var cmd = exec.Command(exePath, "-v")
|
||||
cmd.Stdout = outputBuffer
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var outputString = outputBuffer.String()
|
||||
if len(outputString) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var subMatch = regexp.MustCompile(`\s+v([\d.]+)\s+`).FindStringSubmatch(outputString)
|
||||
if len(subMatch) == 0 {
|
||||
return
|
||||
}
|
||||
fileVersion = subMatch[1]
|
||||
|
||||
// 文件版本是否为最新
|
||||
if fileVersion != teaconst.APINodeVersion {
|
||||
fileVersion = runtimeVersion
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
@@ -7,16 +7,13 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard/dashboardutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"regexp"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
@@ -66,7 +63,7 @@ func (this *IndexAction) RunPost(params struct{}) {
|
||||
|
||||
// 检查当前服务器空间
|
||||
var diskUsageWarning = ""
|
||||
diskPath, diskUsage, diskUsagePercent, shouldWarning := this.checkDiskPartitions(90)
|
||||
diskPath, diskUsage, diskUsagePercent, shouldWarning := dashboardutils.CheckDiskPartitions(90)
|
||||
if shouldWarning {
|
||||
diskUsageWarning = "当前服务器磁盘空间不足,请立即扩充容量,文件路径:" + diskPath + ",已使用:" + types.String(diskUsage/1024/1024/1024) + "G,已使用比例:" + fmt.Sprintf("%.2f%%", diskUsagePercent) + ",仅剩余空间:" + fmt.Sprintf("%.2f%%", 100-diskUsagePercent) + "。"
|
||||
}
|
||||
@@ -263,49 +260,18 @@ func (this *IndexAction) RunPost(params struct{}) {
|
||||
this.Data["metricCharts"] = chartMaps
|
||||
}
|
||||
|
||||
// 当前API节点版本
|
||||
{
|
||||
exePath, runtimeVersion, fileVersion, ok := dashboardutils.CheckLocalAPINode(this.RPC(), this.AdminContext())
|
||||
if ok {
|
||||
this.Data["localLowerVersionAPINode"] = maps.Map{
|
||||
"exePath": exePath,
|
||||
"runtimeVersion": runtimeVersion,
|
||||
"fileVersion": fileVersion,
|
||||
"isRestarting": false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
// 检查服务器磁盘空间
|
||||
func (this *IndexAction) checkDiskPartitions(thresholdPercent float64) (path string, usage uint64, usagePercent float64, shouldWarning bool) {
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
|
||||
return
|
||||
}
|
||||
|
||||
var rootFS = ""
|
||||
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == "/" {
|
||||
rootFS = p.Fstype
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == "/boot" {
|
||||
continue
|
||||
}
|
||||
if p.Fstype != rootFS {
|
||||
continue
|
||||
}
|
||||
stat, _ := disk.Usage(p.Mountpoint)
|
||||
if stat != nil {
|
||||
if stat.Used < 2*uint64(sizes.G) {
|
||||
continue
|
||||
}
|
||||
if stat.UsedPercent > thresholdPercent {
|
||||
path = stat.Path
|
||||
usage = stat.Used
|
||||
usagePercent = stat.UsedPercent
|
||||
shouldWarning = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ func init() {
|
||||
Data("teaMenu", "dashboard").
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
||||
GetPost("", new(IndexAction)).
|
||||
Post("/restartLocalAPINode", new(RestartLocalAPINodeAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RestartLocalAPINodeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *RestartLocalAPINodeAction) RunPost(params struct {
|
||||
ExePath string
|
||||
}) {
|
||||
// 检查当前用户是超级用户
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: this.AdminId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if adminResp.Admin == nil || !adminResp.Admin.IsSuper {
|
||||
this.Fail("请切换到超级用户进行此操作")
|
||||
}
|
||||
|
||||
var exePath = params.ExePath
|
||||
if len(exePath) == 0 {
|
||||
this.Fail("找不到要重启的API节点文件")
|
||||
}
|
||||
|
||||
{
|
||||
var stdoutBuffer = &bytes.Buffer{}
|
||||
var cmd = exec.Command(exePath, "restart")
|
||||
cmd.Stdout = stdoutBuffer
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
this.Fail("运行失败:输出:" + stdoutBuffer.String())
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已启动
|
||||
var countTries = 120
|
||||
for {
|
||||
countTries--
|
||||
if countTries < 0 {
|
||||
this.Fail("启动超时,请尝试手动启动")
|
||||
break
|
||||
}
|
||||
|
||||
var stdoutBuffer = &bytes.Buffer{}
|
||||
var cmd = exec.Command(exePath, "status")
|
||||
cmd.Stdout = stdoutBuffer
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if regexp.MustCompile(`pid:\s*\d+`).
|
||||
MatchString(stdoutBuffer.String()) {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -23,16 +23,26 @@
|
||||
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- 升级提醒 -->
|
||||
<!-- 边缘节点升级提醒 -->
|
||||
<div class="ui icon message error" v-if="!isLoading && nodeUpgradeInfo.count > 0">
|
||||
<i class="icon warning circle"></i>
|
||||
<a href="/clusters">升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级...</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- API节点升级提醒 -->
|
||||
<div class="ui icon message error" v-if="!isLoading && apiNodeUpgradeInfo.count > 0">
|
||||
<i class="icon warning circle"></i>
|
||||
<a href="/api">升级提醒:有 {{apiNodeUpgradeInfo.count}} 个API节点需要升级到 v{{apiNodeUpgradeInfo.version}} 版本;如果已经升级,请尝试重启API节点进程。</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- 本地API节点 -->
|
||||
<div class="ui icon message error" v-if="!isLoading && localLowerVersionAPINode != null">
|
||||
<i class="icon warning circle"></i>
|
||||
<span v-if="localLowerVersionAPINode.runtimeVersion == localLowerVersionAPINode.fileVersion">升级提醒:发现一个正在使用的本地API节点版本需要升级,文件位置:{{localLowerVersionAPINode.exePath}},当前版本:v{{localLowerVersionAPINode.runtimeVersion}}。</span>
|
||||
<span v-if="localLowerVersionAPINode.runtimeVersion != localLowerVersionAPINode.fileVersion">升级提醒:发现一个正在使用的本地API节点版本文件已经更新,但需要重启后生效,文件位置:{{localLowerVersionAPINode.exePath}}。 <a href="" @click.prevent="restartAPINode" v-if="!isRestartingLocalAPINode">[帮我重启]</a><span v-if="isRestartingLocalAPINode">尝试重启中...</span></span>
|
||||
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- 没有磁盘空间提醒 -->
|
||||
<div class="ui icon message error" v-if="!isLoading && dashboard.diskUsageWarning != null && dashboard.diskUsageWarning.length > 0">
|
||||
<i class="icon warning circle"></i>
|
||||
|
||||
@@ -3,6 +3,7 @@ Tea.context(function () {
|
||||
this.trafficTab = "hourly"
|
||||
this.metricCharts = []
|
||||
this.dashboard = {}
|
||||
this.localLowerVersionAPINode = null
|
||||
|
||||
this.$delay(function () {
|
||||
this.$post("$")
|
||||
@@ -193,4 +194,29 @@ Tea.context(function () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重启本地API节点
|
||||
this.isRestartingLocalAPINode = false
|
||||
this.restartAPINode = function () {
|
||||
if (this.isRestartingLocalAPINode) {
|
||||
return
|
||||
}
|
||||
if (this.localLowerVersionAPINode == null) {
|
||||
return
|
||||
}
|
||||
this.isRestartingLocalAPINode = true
|
||||
this.localLowerVersionAPINode.isRestarting = true
|
||||
this.$post("/dashboard/restartLocalAPINode")
|
||||
.params({
|
||||
"exePath": this.localLowerVersionAPINode.exePath
|
||||
})
|
||||
.timeout(300)
|
||||
.success(function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
.done(function () {
|
||||
this.isRestartingLocalAPINode = false
|
||||
this.localLowerVersionAPINode.isRestarting = false
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user