From 2d05ad445996117ea18c87029d27abc75fcb29e0 Mon Sep 17 00:00:00 2001 From: GoEdgeLab Date: Sun, 21 Jan 2024 16:55:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=9B=E5=BB=BA=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E8=8A=82=E7=82=B9=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/edge-admin/main.go | 16 ++- .../clusters/cluster/node/nodeutils/utils.go | 132 ++++++++++++++++++ .../cluster/node/nodeutils/utils_test.go | 16 +++ 3 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 internal/web/actions/default/clusters/cluster/node/nodeutils/utils_test.go diff --git a/cmd/edge-admin/main.go b/cmd/edge-admin/main.go index c1844a3f..4aade853 100644 --- a/cmd/edge-admin/main.go +++ b/cmd/edge-admin/main.go @@ -11,6 +11,7 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/nodes" "github.com/TeaOSLab/EdgeAdmin/internal/utils" _ "github.com/TeaOSLab/EdgeAdmin/internal/web" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" _ "github.com/TeaOSLab/EdgeCommon/pkg/langs/messages" "github.com/iwind/TeaGo/Tea" _ "github.com/iwind/TeaGo/bootstrap" @@ -40,7 +41,8 @@ func main() { Option("demo", "switch to demo mode"). Option("dev", "switch to 'dev' mode"). Option("prod", "switch to 'prod' mode"). - Option("upgrade [--url=URL]", "upgrade from official site or an url") + Option("upgrade [--url=URL]", "upgrade from official site or an url"). + Option("install-local-node", "install a local node") app.On("daemon", func() { nodes.NewAdminNode().Daemon() @@ -76,7 +78,7 @@ func main() { fmt.Println("done") }) app.On("recover", func() { - sock := gosock.NewTmpSock(teaconst.ProcessName) + var sock = gosock.NewTmpSock(teaconst.ProcessName) if !sock.IsListening() { fmt.Println("[ERROR]the service not started yet, you should start the service first") return @@ -89,7 +91,7 @@ func main() { fmt.Println("enter recovery mode successfully") }) app.On("demo", func() { - sock := gosock.NewTmpSock(teaconst.ProcessName) + var sock = gosock.NewTmpSock(teaconst.ProcessName) if !sock.IsListening() { fmt.Println("[ERROR]the service not started yet, you should start the service first") return @@ -178,6 +180,14 @@ func main() { log.Println("restarting ...") app.RunRestart() }) + app.On("install-local-node", func() { + err := nodeutils.InstallLocalNode() + if err != nil { + fmt.Println("[ERROR]" + err.Error()) + return + } + fmt.Println("success") + }) app.Run(func() { var adminNode = nodes.NewAdminNode() adminNode.Run() diff --git a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go index f147c768..9e8e7d90 100644 --- a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go +++ b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go @@ -4,12 +4,23 @@ package nodeutils import ( "errors" + "fmt" + "github.com/TeaOSLab/EdgeAdmin/internal/configs" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/rpc" + "github.com/TeaOSLab/EdgeAdmin/internal/utils" + executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/langs/codes" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/types" + "gopkg.in/yaml.v3" + "os" + "runtime" "strconv" + "time" ) // InitNodeInfo 初始化节点信息 @@ -104,3 +115,124 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod return nodeResp.Node, nil } + +// InstallLocalNode 安装本地节点 +func InstallLocalNode() error { + var targetDir = Tea.Root + var nodeDir = targetDir + "/edge-node" + var apiAddr = "http://127.0.0.1:8001" // 先固定 + + // 查找节点安装文件 + var zipFile = Tea.Root + "/edge-api/deploy/edge-node-linux-" + runtime.GOARCH + "-v" + teaconst.Version /** 默认和管理系统一致 **/ + ".zip" + { + stat, err := os.Stat(zipFile) + if err != nil { + if os.IsNotExist(err) { + return errors.New("installer file not found in '" + zipFile + "'") + } + return fmt.Errorf("open installer file failed: %w", err) + } + if stat.IsDir() { + return errors.New("invalid installer file '" + zipFile + "'") + } + } + + // 解压节点 + var unzip = utils.NewUnzip(zipFile, targetDir) + err := unzip.Run() + if err != nil { + return fmt.Errorf("unzip installer file failed: %w", err) + } + + // 创建节点 + rpcClient, err := rpc.SharedRPC() + if err != nil { + return fmt.Errorf("create rpc client failed: %w", err) + } + var ctx = rpcClient.Context(0) + nodeClustersResp, err := rpcClient.NodeClusterRPC().ListEnabledNodeClusters(ctx, &pb.ListEnabledNodeClustersRequest{ + IdDesc: true, + Offset: 0, + Size: 1, + }) + if err != nil { + return err + } + if len(nodeClustersResp.NodeClusters) == 0 { + return errors.New("no clusters yet, please create a cluster at least") + } + var clusterId = nodeClustersResp.NodeClusters[0].Id + + // 检查节点是否已生成 + countNodesResp, err := rpcClient.NodeRPC().CountAllEnabledNodesMatch(ctx, &pb.CountAllEnabledNodesMatchRequest{ + NodeClusterId: clusterId, + }) + if err != nil { + return err + } + if countNodesResp.Count > 0 { + // 这里先不判断是否有本地节点,只要有节点,就不允许再次执行 + return errors.New("there are already nodes in the cluster") + } + + createNodeResp, err := rpcClient.NodeRPC().CreateNode(ctx, &pb.CreateNodeRequest{ + Name: "本地节点", + NodeClusterId: clusterId, + NodeLogin: nil, + NodeGroupId: 0, + DnsRoutes: nil, + NodeRegionId: 0, + }) + if err != nil { + return err + } + var nodeId = createNodeResp.NodeId + nodeResp, err := rpcClient.NodeRPC().FindEnabledNode(ctx, &pb.FindEnabledNodeRequest{NodeId: nodeId}) + if err != nil { + return err + } + if nodeResp.Node == nil { + return errors.New("could not find local node with created id '" + types.String(nodeId) + "'") + } + var node = nodeResp.Node + + // 生成节点配置 + var apiConfig = &configs.APIConfig{ + RPCEndpoints: []string{apiAddr}, + RPCDisableUpdate: true, + NodeId: node.UniqueId, + Secret: node.Secret, + } + apiConfigYAML, err := yaml.Marshal(apiConfig) + if err != nil { + return fmt.Errorf("encode config failed: %w", err) + } + err = os.WriteFile(nodeDir+"/configs/api_node.yaml", apiConfigYAML, 0666) + if err != nil { + return fmt.Errorf("write config file failed: %w", err) + } + + // 测试节点 + { + var cmd = executils.NewTimeoutCmd(5*time.Second, nodeDir+"/bin/edge-node", "test") + cmd.WithStdout() + cmd.WithStderr() + err = cmd.Run() + if err != nil { + return fmt.Errorf("node test failed: %w", err) + } + } + + // 启动节点 + { + var cmd = executils.NewTimeoutCmd(5*time.Second, nodeDir+"/bin/edge-node", "start") + cmd.WithStdout() + cmd.WithStderr() + err = cmd.Run() + if err != nil { + return fmt.Errorf("node start failed: %w", err) + } + } + + return nil +} diff --git a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_test.go b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_test.go new file mode 100644 index 00000000..3a771684 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_test.go @@ -0,0 +1,16 @@ +// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package nodeutils_test + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" + _ "github.com/iwind/TeaGo/bootstrap" + "testing" +) + +func TestInstallLocalNode(t *testing.T) { + err := nodeutils.InstallLocalNode() + if err != nil { + t.Fatal(err) + } +}