1.4.5.2
This commit is contained in:
79
EdgeAdmin/internal/utils/apinodeutils/deploy_file.go
Normal file
79
EdgeAdmin/internal/utils/apinodeutils/deploy_file.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DeployFile 部署文件描述
|
||||
type DeployFile struct {
|
||||
OS string
|
||||
Arch string
|
||||
Version string
|
||||
Path string
|
||||
}
|
||||
|
||||
// Sum 计算概要
|
||||
func (this *DeployFile) Sum() (string, error) {
|
||||
fp, err := os.Open(this.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
m := md5.New()
|
||||
buffer := make([]byte, 128*1024)
|
||||
for {
|
||||
n, err := fp.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
_, err = m.Write(buffer[:n])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
sum := m.Sum(nil)
|
||||
return fmt.Sprintf("%x", sum), nil
|
||||
}
|
||||
|
||||
// Read 读取一个片段数据
|
||||
func (this *DeployFile) Read(offset int64) (data []byte, newOffset int64, err error) {
|
||||
fp, err := os.Open(this.Path)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
stat, err := fp.Stat()
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
if offset >= stat.Size() {
|
||||
return nil, offset, io.EOF
|
||||
}
|
||||
|
||||
_, err = fp.Seek(offset, io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
|
||||
buffer := make([]byte, 128*1024)
|
||||
n, err := fp.Read(buffer)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
|
||||
return buffer[:n], offset + int64(n), nil
|
||||
}
|
||||
96
EdgeAdmin/internal/utils/apinodeutils/deploy_manager.go
Normal file
96
EdgeAdmin/internal/utils/apinodeutils/deploy_manager.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// DeployManager 节点部署文件管理器
|
||||
// 如果节点部署文件有变化,需要重启API节点以便于生效
|
||||
type DeployManager struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewDeployManager 获取新节点部署文件管理器
|
||||
func NewDeployManager() *DeployManager {
|
||||
var manager = &DeployManager{
|
||||
dir: Tea.Root + "/edge-api/deploy",
|
||||
}
|
||||
manager.LoadNodeFiles()
|
||||
manager.LoadNSNodeFiles()
|
||||
return manager
|
||||
}
|
||||
|
||||
// LoadNodeFiles 加载所有边缘节点文件
|
||||
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
|
||||
var keyMap = map[string]*DeployFile{} // key => File
|
||||
|
||||
var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
||||
for _, file := range files.NewFile(this.dir).List() {
|
||||
var name = file.Name()
|
||||
if !reg.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
var matches = reg.FindStringSubmatch(name)
|
||||
var osName = matches[1]
|
||||
var arch = matches[2]
|
||||
var version = matches[3]
|
||||
|
||||
var key = osName + "_" + arch
|
||||
oldFile, ok := keyMap[key]
|
||||
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
||||
continue
|
||||
}
|
||||
keyMap[key] = &DeployFile{
|
||||
OS: osName,
|
||||
Arch: arch,
|
||||
Version: version,
|
||||
Path: file.Path(),
|
||||
}
|
||||
}
|
||||
|
||||
var result = []*DeployFile{}
|
||||
for _, v := range keyMap {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// LoadNSNodeFiles 加载所有NS节点安装文件
|
||||
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
|
||||
var keyMap = map[string]*DeployFile{} // key => File
|
||||
|
||||
var reg = regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
||||
for _, file := range files.NewFile(this.dir).List() {
|
||||
var name = file.Name()
|
||||
if !reg.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
var matches = reg.FindStringSubmatch(name)
|
||||
var osName = matches[1]
|
||||
var arch = matches[2]
|
||||
var version = matches[3]
|
||||
|
||||
var key = osName + "_" + arch
|
||||
oldFile, ok := keyMap[key]
|
||||
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
||||
continue
|
||||
}
|
||||
keyMap[key] = &DeployFile{
|
||||
OS: osName,
|
||||
Arch: arch,
|
||||
Version: version,
|
||||
Path: file.Path(),
|
||||
}
|
||||
}
|
||||
|
||||
var result = []*DeployFile{}
|
||||
for _, v := range keyMap {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
30
EdgeAdmin/internal/utils/apinodeutils/manager.go
Normal file
30
EdgeAdmin/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)
|
||||
}
|
||||
349
EdgeAdmin/internal/utils/apinodeutils/upgrader.go
Normal file
349
EdgeAdmin/internal/utils/apinodeutils/upgrader.go
Normal file
@@ -0,0 +1,349 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Progress struct {
|
||||
Percent float64
|
||||
}
|
||||
|
||||
type Upgrader struct {
|
||||
progress *Progress
|
||||
apiExe string
|
||||
apiNodeId int64
|
||||
}
|
||||
|
||||
func NewUpgrader(apiNodeId int64) *Upgrader {
|
||||
return &Upgrader{
|
||||
apiExe: apiExe(),
|
||||
progress: &Progress{Percent: 0},
|
||||
apiNodeId: apiNodeId,
|
||||
}
|
||||
}
|
||||
|
||||
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: 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(this.apiNodeId) + "'")
|
||||
}
|
||||
|
||||
apiConfig, err := configs.LoadAPIConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var newAPIConfig = apiConfig.Clone()
|
||||
newAPIConfig.RPCEndpoints = apiNode.AccessAddrs
|
||||
|
||||
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级边缘节点
|
||||
err = this.upgradeNodes(sharedClient.Context(0), rpcClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级NS节点
|
||||
err = this.upgradeNSNodes(sharedClient.Context(0), rpcClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级API节点
|
||||
err = this.upgradeAPINode(sharedClient.Context(0), rpcClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upgrade api node failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Progress 查看升级进程
|
||||
func (this *Upgrader) Progress() *Progress {
|
||||
return this.progress
|
||||
}
|
||||
|
||||
// 升级API节点
|
||||
func (this *Upgrader) upgradeAPINode(ctx context.Context, rpcClient *rpc.RPCClient) error {
|
||||
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(ctx, &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !Tea.IsTesting() /** 开发环境下允许突破此限制方便测试 **/ &&
|
||||
(stringutil.VersionCompare(versionResp.Version, "0.6.4" /** 从0.6.4开始支持 **/) < 0 || versionResp.Os != runtime.GOOS || versionResp.Arch != runtime.GOARCH) {
|
||||
return errors.New("could not upgrade api node v" + versionResp.Version + "/" + versionResp.Os + "/" + versionResp.Arch)
|
||||
}
|
||||
|
||||
// 检查本地文件版本
|
||||
canUpgrade, reason := CanUpgrade(versionResp.Version, versionResp.Os, versionResp.Arch)
|
||||
if !canUpgrade {
|
||||
return errors.New(reason)
|
||||
}
|
||||
|
||||
localVersion, err := lookupLocalVersion()
|
||||
if err != nil {
|
||||
return fmt.Errorf("lookup version failed: %w", err)
|
||||
}
|
||||
|
||||
// 检查要升级的文件
|
||||
var gzFile = this.apiExe + "." + localVersion + ".gz"
|
||||
|
||||
gzReader, err := os.Open(gzFile)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
err = func() error {
|
||||
// 压缩文件
|
||||
exeReader, err := os.Open(this.apiExe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = exeReader.Close()
|
||||
}()
|
||||
var tmpGzFile = gzFile + ".tmp"
|
||||
gzFileWriter, err := os.OpenFile(tmpGzFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var gzWriter = gzip.NewWriter(gzFileWriter)
|
||||
defer func() {
|
||||
_ = gzWriter.Close()
|
||||
_ = gzFileWriter.Close()
|
||||
|
||||
_ = os.Rename(tmpGzFile, gzFile)
|
||||
}()
|
||||
_, err = io.Copy(gzWriter, exeReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gzReader, err = os.Open(gzFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = gzReader.Close()
|
||||
}()
|
||||
|
||||
// 开始上传
|
||||
var hash = md5.New()
|
||||
var buf = make([]byte, 128*4096)
|
||||
var isFirst = true
|
||||
stat, err := gzReader.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var totalSize = stat.Size()
|
||||
if totalSize == 0 {
|
||||
_ = gzReader.Close()
|
||||
_ = os.Remove(gzFile)
|
||||
return errors.New("invalid gz file")
|
||||
}
|
||||
|
||||
var uploadedSize int64 = 0
|
||||
for {
|
||||
n, err := gzReader.Read(buf)
|
||||
if n > 0 {
|
||||
// 计算Hash
|
||||
hash.Write(buf[:n])
|
||||
|
||||
// 上传
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadAPINodeFile(rpcClient.Context(0), &pb.UploadAPINodeFileRequest{
|
||||
Filename: filepath.Base(this.apiExe),
|
||||
Sum: "",
|
||||
ChunkData: buf[:n],
|
||||
IsFirstChunk: isFirst,
|
||||
IsLastChunk: false,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
// 进度
|
||||
uploadedSize += int64(n)
|
||||
this.progress = &Progress{Percent: float64(uploadedSize*100) / float64(totalSize)}
|
||||
}
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
if err == io.EOF {
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadAPINodeFile(rpcClient.Context(0), &pb.UploadAPINodeFileRequest{
|
||||
Filename: filepath.Base(this.apiExe),
|
||||
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
|
||||
ChunkData: buf[:n],
|
||||
IsFirstChunk: isFirst,
|
||||
IsLastChunk: true,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 升级边缘节点
|
||||
func (this *Upgrader) upgradeNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
|
||||
// 本地的
|
||||
var manager = NewDeployManager()
|
||||
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
|
||||
for _, deployFile := range manager.LoadNodeFiles() {
|
||||
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
|
||||
}
|
||||
|
||||
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
|
||||
for _, nodeFile := range remoteFilesResp.NodeDeployFiles {
|
||||
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
|
||||
}
|
||||
|
||||
// 对比版本
|
||||
for key, deployFile := range localFileMap {
|
||||
remoteDeployFile, ok := remoteFileMap[key]
|
||||
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
|
||||
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload deploy file '%s' failed: %w", filepath.Base(deployFile.Path), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 升级NS节点
|
||||
func (this *Upgrader) upgradeNSNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
|
||||
// 本地的
|
||||
var manager = NewDeployManager()
|
||||
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
|
||||
for _, deployFile := range manager.LoadNSNodeFiles() {
|
||||
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
|
||||
}
|
||||
|
||||
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
|
||||
for _, nodeFile := range remoteFilesResp.NsNodeDeployFiles {
|
||||
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
|
||||
}
|
||||
|
||||
// 对比版本
|
||||
for key, deployFile := range localFileMap {
|
||||
remoteDeployFile, ok := remoteFileMap[key]
|
||||
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
|
||||
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload deploy file '%s' failed: %w", filepath.Base(deployFile.Path), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 上传节点文件
|
||||
func (this *Upgrader) uploadNodeDeployFile(ctx context.Context, rpcClient *rpc.RPCClient, path string) error {
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
var buf = make([]byte, 128*4096)
|
||||
var isFirst = true
|
||||
|
||||
var hash = md5.New()
|
||||
|
||||
for {
|
||||
n, err := fp.Read(buf)
|
||||
if n > 0 {
|
||||
hash.Write(buf[:n])
|
||||
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
|
||||
Filename: filepath.Base(path),
|
||||
Sum: "",
|
||||
ChunkData: buf[:n],
|
||||
IsFirstChunk: isFirst,
|
||||
IsLastChunk: false,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
isFirst = false
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
|
||||
Filename: filepath.Base(path),
|
||||
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
|
||||
ChunkData: nil,
|
||||
IsFirstChunk: false,
|
||||
IsLastChunk: true,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
22
EdgeAdmin/internal/utils/apinodeutils/upgrader_test.go
Normal file
22
EdgeAdmin/internal/utils/apinodeutils/upgrader_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpgrader_CanUpgrade(t *testing.T) {
|
||||
t.Log(apinodeutils.CanUpgrade("0.6.3", runtime.GOOS, runtime.GOARCH))
|
||||
}
|
||||
|
||||
func TestUpgrader_Upgrade(t *testing.T) {
|
||||
var upgrader = apinodeutils.NewUpgrader(1)
|
||||
err := upgrader.Upgrade()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
77
EdgeAdmin/internal/utils/apinodeutils/utils.go
Normal file
77
EdgeAdmin/internal/utils/apinodeutils/utils.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// 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 stringutil.VersionCompare(apiVersion, "0.6.4") < 0 {
|
||||
return false, "api node version must greater than or equal to 0.6.4"
|
||||
}
|
||||
|
||||
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 := lookupLocalVersion()
|
||||
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 lookupLocalVersion() (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"
|
||||
}
|
||||
Reference in New Issue
Block a user