mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-03 06:24:03 +08:00
可以在集群中查看待安装节点、并直接安装节点
This commit is contained in:
@@ -60,7 +60,7 @@ function build() {
|
|||||||
architects=("amd64" "386")
|
architects=("amd64" "386")
|
||||||
for arch in "${architects[@]}"; do
|
for arch in "${architects[@]}"; do
|
||||||
# TODO support arm, mips ...
|
# TODO support arm, mips ...
|
||||||
env GOOS=linux GOARCH=${arch} go build --ldflags="-s -w" -o $ROOT/installers/installer-helper-linux-${arch} $ROOT/../cmd/installer-helper/main.go
|
env GOOS=linux GOARCH=${arch} go build --ldflags="-s -w" -o $ROOT/installers/edge-installer-helper-linux-${arch} $ROOT/../cmd/installer-helper/main.go
|
||||||
done
|
done
|
||||||
|
|
||||||
# building api node
|
# building api node
|
||||||
|
|||||||
2
build/installers/.gitignore
vendored
2
build/installers/.gitignore
vendored
@@ -1 +1 @@
|
|||||||
installer-*
|
edge-*
|
||||||
@@ -171,6 +171,19 @@ func (this *APINodeDAO) FindAllEnabledAPINodes() (result []*APINode, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 列出所有可用而且启用的API节点
|
||||||
|
func (this *APINodeDAO) FindAllEnabledAndOnAPINodes() (result []*APINode, err error) {
|
||||||
|
_, err = this.Query().
|
||||||
|
Attr("clusterId", 0). // 非集群专用
|
||||||
|
Attr("isOn", true).
|
||||||
|
State(APINodeStateEnabled).
|
||||||
|
Desc("order").
|
||||||
|
AscPk().
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 计算API节点数量
|
// 计算API节点数量
|
||||||
func (this *APINodeDAO) CountAllEnabledAPINodes() (int64, error) {
|
func (this *APINodeDAO) CountAllEnabledAPINodes() (int64, error) {
|
||||||
return this.Query().
|
return this.Query().
|
||||||
|
|||||||
@@ -268,6 +268,14 @@ func (this *NodeClusterDAO) FindAllEnabledClustersWithGrantId(grantId int64) (re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找集群的认证ID
|
||||||
|
func (this *NodeClusterDAO) FindClusterGrantId(clusterId int64) (int64, error) {
|
||||||
|
return this.Query().
|
||||||
|
Pk(clusterId).
|
||||||
|
Result("grantId").
|
||||||
|
FindInt64Col(0)
|
||||||
|
}
|
||||||
|
|
||||||
// 生成唯一ID
|
// 生成唯一ID
|
||||||
func (this *NodeClusterDAO) genUniqueId() (string, error) {
|
func (this *NodeClusterDAO) genUniqueId() (string, error) {
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -376,21 +376,33 @@ func (this *NodeDAO) UpdateNodeIsInstalled(nodeId int64, isInstalled bool) error
|
|||||||
|
|
||||||
// 查询节点的安装状态
|
// 查询节点的安装状态
|
||||||
func (this *NodeDAO) FindNodeInstallStatus(nodeId int64) (*NodeInstallStatus, error) {
|
func (this *NodeDAO) FindNodeInstallStatus(nodeId int64) (*NodeInstallStatus, error) {
|
||||||
installStatus, err := this.Query().
|
node, err := this.Query().
|
||||||
Pk(nodeId).
|
Pk(nodeId).
|
||||||
Result("installStatus").
|
Result("installStatus", "isInstalled").
|
||||||
FindStringCol("")
|
Find()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if node == nil {
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
installStatus := node.(*Node).InstallStatus
|
||||||
|
isInstalled := node.(*Node).IsInstalled == 1
|
||||||
if len(installStatus) == 0 {
|
if len(installStatus) == 0 {
|
||||||
return NewNodeInstallStatus(), nil
|
return NewNodeInstallStatus(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
status := &NodeInstallStatus{}
|
status := &NodeInstallStatus{}
|
||||||
err = json.Unmarshal([]byte(installStatus), status)
|
err = json.Unmarshal([]byte(installStatus), status)
|
||||||
return status, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isInstalled {
|
||||||
|
status.IsFinished = true
|
||||||
|
status.IsOk = true
|
||||||
|
}
|
||||||
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改节点的安装状态
|
// 修改节点的安装状态
|
||||||
@@ -524,6 +536,18 @@ func (this *NodeDAO) FindAllEnabledNodesWithGrantId(grantId int64) (result []*No
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找所有未安装的节点
|
||||||
|
func (this *NodeDAO) FindAllNotInstalledNodesWithClusterId(clusterId int64) (result []*Node, err error) {
|
||||||
|
_, err = this.Query().
|
||||||
|
State(NodeStateEnabled).
|
||||||
|
Attr("clusterId", clusterId).
|
||||||
|
Attr("isInstalled", false).
|
||||||
|
DescPk().
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 生成唯一ID
|
// 生成唯一ID
|
||||||
func (this *NodeDAO) genUniqueId() (string, error) {
|
func (this *NodeDAO) genUniqueId() (string, error) {
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ type NodeInstallStatus struct {
|
|||||||
IsFinished bool `json:"isFinished"` // 是否已结束
|
IsFinished bool `json:"isFinished"` // 是否已结束
|
||||||
IsOk bool `json:"isOk"` // 是否正确安装
|
IsOk bool `json:"isOk"` // 是否正确安装
|
||||||
Error string `json:"error"` // 错误信息
|
Error string `json:"error"` // 错误信息
|
||||||
|
ErrorCode string `json:"errorCode"` // 错误代号
|
||||||
UpdatedAt int64 `json:"updatedAt"` // 更新时间,安装过程中需要每隔N秒钟更新这个状态,以便于让系统知道安装仍在进行中
|
UpdatedAt int64 `json:"updatedAt"` // 更新时间,安装过程中需要每隔N秒钟更新这个状态,以便于让系统知道安装仍在进行中
|
||||||
Steps []*NodeInstallStatusStep `json:"steps"` // 步骤
|
Steps []*NodeInstallStatusStep `json:"steps"` // 步骤
|
||||||
}
|
}
|
||||||
|
|||||||
21
internal/errors/detailed_error.go
Normal file
21
internal/errors/detailed_error.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
type DetailedError struct {
|
||||||
|
msg string
|
||||||
|
code string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DetailedError) Error() string {
|
||||||
|
return this.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DetailedError) Code() string {
|
||||||
|
return this.code
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDetailedError(code string, error string) *DetailedError {
|
||||||
|
return &DetailedError{
|
||||||
|
msg: error,
|
||||||
|
code: code,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -151,12 +151,12 @@ func (this *BaseInstaller) InstallHelper(targetDir string) (env *Env, err error)
|
|||||||
archName = "386"
|
archName = "386"
|
||||||
}
|
}
|
||||||
|
|
||||||
exeName := "installer-helper-" + osName + "-" + archName
|
exeName := "edge-installer-helper-" + osName + "-" + archName
|
||||||
exePath := Tea.Root + "/installers/" + exeName
|
exePath := Tea.Root + "/installers/" + exeName
|
||||||
|
|
||||||
err = this.client.Copy(exePath, targetDir+"/"+exeName, 0777)
|
err = this.client.Copy(exePath, targetDir+"/"+exeName, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env, err
|
return env, errors.New("copy '" + exeName + "' to '" + targetDir + "' failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
env = &Env{
|
env = &Env{
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ func (this *NodeInstaller) Install(dir string, params interface{}) error {
|
|||||||
return errors.New("params validation: " + err.Error())
|
return errors.New("params validation: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查目标目录是否存在
|
||||||
|
_, err = this.client.Stat(dir)
|
||||||
|
if err != nil {
|
||||||
|
err = this.client.MkdirAll(dir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("create directory '" + dir + "' failed: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 安装助手
|
// 安装助手
|
||||||
env, err := this.InstallHelper(dir)
|
env, err := this.InstallHelper(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ func (this *Queue) InstallNodeProcess(nodeId int64) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 开始安装
|
// 开始安装
|
||||||
err = this.InstallNode(nodeId)
|
err = this.InstallNode(nodeId, installStatus)
|
||||||
|
|
||||||
// 安装结束
|
// 安装结束
|
||||||
installStatus.IsRunning = false
|
installStatus.IsRunning = false
|
||||||
@@ -78,13 +78,13 @@ func (this *Queue) InstallNodeProcess(nodeId int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 安装边缘节点
|
// 安装边缘节点
|
||||||
func (this *Queue) InstallNode(nodeId int64) error {
|
func (this *Queue) InstallNode(nodeId int64, installStatus *models.NodeInstallStatus) error {
|
||||||
node, err := models.SharedNodeDAO.FindEnabledNode(nodeId)
|
node, err := models.SharedNodeDAO.FindEnabledNode(nodeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return errors.New("can not find node, ID:'" + strconv.FormatInt(nodeId, 10) + "'")
|
return errors.New("can not find node, ID:'" + numberutils.FormatInt64(nodeId) + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登录信息
|
// 登录信息
|
||||||
@@ -93,6 +93,7 @@ func (this *Queue) InstallNode(nodeId int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if login == nil {
|
if login == nil {
|
||||||
|
installStatus.ErrorCode = "EMPTY_LOGIN"
|
||||||
return errors.New("can not find node login information")
|
return errors.New("can not find node login information")
|
||||||
}
|
}
|
||||||
loginParams, err := login.DecodeSSHParams()
|
loginParams, err := login.DecodeSSHParams()
|
||||||
@@ -100,12 +101,35 @@ func (this *Queue) InstallNode(nodeId int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(loginParams.Host) == 0 {
|
||||||
|
installStatus.ErrorCode = "EMPTY_SSH_HOST"
|
||||||
|
return errors.New("ssh host should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginParams.Port <= 0 {
|
||||||
|
installStatus.ErrorCode = "EMPTY_SSH_PORT"
|
||||||
|
return errors.New("ssh port is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginParams.GrantId == 0 {
|
||||||
|
// 从集群中读取
|
||||||
|
grantId, err := models.SharedNodeClusterDAO.FindClusterGrantId(int64(node.ClusterId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if grantId == 0 {
|
||||||
|
installStatus.ErrorCode = "EMPTY_GRANT"
|
||||||
|
return errors.New("can not find node grant")
|
||||||
|
}
|
||||||
|
loginParams.GrantId = grantId
|
||||||
|
}
|
||||||
grant, err := models.SharedNodeGrantDAO.FindEnabledNodeGrant(loginParams.GrantId)
|
grant, err := models.SharedNodeGrantDAO.FindEnabledNodeGrant(loginParams.GrantId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if grant == nil {
|
if grant == nil {
|
||||||
return errors.New("can not find user grant with id '" + strconv.FormatInt(loginParams.GrantId, 10) + "'")
|
installStatus.ErrorCode = "EMPTY_GRANT"
|
||||||
|
return errors.New("can not find user grant with id '" + numberutils.FormatInt64(loginParams.GrantId) + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安装目录
|
// 安装目录
|
||||||
@@ -121,12 +145,13 @@ func (this *Queue) InstallNode(nodeId int64) error {
|
|||||||
}
|
}
|
||||||
installDir = cluster.InstallDir
|
installDir = cluster.InstallDir
|
||||||
if len(installDir) == 0 {
|
if len(installDir) == 0 {
|
||||||
return errors.New("unable to find installation dir")
|
// 默认是 $登录用户/edge-node
|
||||||
|
installDir = "/" + grant.Username + "/edge-node"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// API终端
|
// API终端
|
||||||
apiNodes, err := models.SharedAPINodeDAO.FindAllEnabledAPINodes()
|
apiNodes, err := models.SharedAPINodeDAO.FindAllEnabledAndOnAPINodes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ func (this *NodeService) ListEnabledNodesMatch(ctx context.Context, req *pb.List
|
|||||||
IsFinished: installStatus.IsFinished,
|
IsFinished: installStatus.IsFinished,
|
||||||
IsOk: installStatus.IsOk,
|
IsOk: installStatus.IsOk,
|
||||||
Error: installStatus.Error,
|
Error: installStatus.Error,
|
||||||
|
ErrorCode: installStatus.ErrorCode,
|
||||||
UpdatedAt: installStatus.UpdatedAt,
|
UpdatedAt: installStatus.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,6 +296,7 @@ func (this *NodeService) FindEnabledNode(ctx context.Context, req *pb.FindEnable
|
|||||||
IsFinished: installStatus.IsFinished,
|
IsFinished: installStatus.IsFinished,
|
||||||
IsOk: installStatus.IsOk,
|
IsOk: installStatus.IsOk,
|
||||||
Error: installStatus.Error,
|
Error: installStatus.Error,
|
||||||
|
ErrorCode: installStatus.ErrorCode,
|
||||||
UpdatedAt: installStatus.UpdatedAt,
|
UpdatedAt: installStatus.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,3 +483,131 @@ func (this *NodeService) FindAllEnabledNodesWithGrantId(ctx context.Context, req
|
|||||||
|
|
||||||
return &pb.FindAllEnabledNodesWithGrantIdResponse{Nodes: result}, nil
|
return &pb.FindAllEnabledNodesWithGrantIdResponse{Nodes: result}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 列出所有未安装的节点
|
||||||
|
func (this *NodeService) FindAllNotInstalledNodesWithClusterId(ctx context.Context, req *pb.FindAllNotInstalledNodesWithClusterIdRequest) (*pb.FindAllNotInstalledNodesWithClusterIdResponse, error) {
|
||||||
|
// 校验请求
|
||||||
|
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes, err := models.SharedNodeDAO.FindAllNotInstalledNodesWithClusterId(req.ClusterId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := []*pb.Node{}
|
||||||
|
for _, node := range nodes {
|
||||||
|
// 认证信息
|
||||||
|
login, err := models.SharedNodeLoginDAO.FindEnabledNodeLoginWithNodeId(int64(node.Id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var pbLogin *pb.NodeLogin = nil
|
||||||
|
if login != nil {
|
||||||
|
pbLogin = &pb.NodeLogin{
|
||||||
|
Id: int64(login.Id),
|
||||||
|
Name: login.Name,
|
||||||
|
Type: login.Type,
|
||||||
|
Params: []byte(login.Params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP信息
|
||||||
|
addresses, err := models.SharedNodeIPAddressDAO.FindAllEnabledAddressesWithNode(int64(node.Id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pbAddresses := []*pb.NodeIPAddress{}
|
||||||
|
for _, address := range addresses {
|
||||||
|
pbAddresses = append(pbAddresses, &pb.NodeIPAddress{
|
||||||
|
Id: int64(address.Id),
|
||||||
|
NodeId: int64(address.NodeId),
|
||||||
|
Name: address.Name,
|
||||||
|
Ip: address.Ip,
|
||||||
|
Description: address.Description,
|
||||||
|
State: int64(address.State),
|
||||||
|
Order: int64(address.Order),
|
||||||
|
CanAccess: address.CanAccess == 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安装信息
|
||||||
|
installStatus, err := node.DecodeInstallStatus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pbInstallStatus := &pb.NodeInstallStatus{}
|
||||||
|
if installStatus != nil {
|
||||||
|
pbInstallStatus = &pb.NodeInstallStatus{
|
||||||
|
IsRunning: installStatus.IsRunning,
|
||||||
|
IsFinished: installStatus.IsFinished,
|
||||||
|
IsOk: installStatus.IsOk,
|
||||||
|
Error: installStatus.Error,
|
||||||
|
ErrorCode: installStatus.ErrorCode,
|
||||||
|
UpdatedAt: installStatus.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, &pb.Node{
|
||||||
|
Id: int64(node.Id),
|
||||||
|
Name: node.Name,
|
||||||
|
Version: int64(node.Version),
|
||||||
|
IsInstalled: node.IsInstalled == 1,
|
||||||
|
Status: node.Status,
|
||||||
|
IsOn: node.IsOn == 1,
|
||||||
|
Login: pbLogin,
|
||||||
|
IpAddresses: pbAddresses,
|
||||||
|
InstallStatus: pbInstallStatus,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &pb.FindAllNotInstalledNodesWithClusterIdResponse{Nodes: result}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取节点安装状态
|
||||||
|
func (this *NodeService) FindNodeInstallStatus(ctx context.Context, req *pb.FindNodeInstallStatusRequest) (*pb.FindNodeInstallStatusResponse, error) {
|
||||||
|
// 校验请求
|
||||||
|
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
installStatus, err := models.SharedNodeDAO.FindNodeInstallStatus(req.NodeId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if installStatus == nil {
|
||||||
|
return &pb.FindNodeInstallStatusResponse{InstallStatus: nil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pbInstallStatus := &pb.NodeInstallStatus{
|
||||||
|
IsRunning: installStatus.IsRunning,
|
||||||
|
IsFinished: installStatus.IsFinished,
|
||||||
|
IsOk: installStatus.IsOk,
|
||||||
|
Error: installStatus.Error,
|
||||||
|
ErrorCode: installStatus.ErrorCode,
|
||||||
|
UpdatedAt: installStatus.UpdatedAt,
|
||||||
|
}
|
||||||
|
return &pb.FindNodeInstallStatusResponse{InstallStatus: pbInstallStatus}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改节点登录信息
|
||||||
|
func (this *NodeService) UpdateNodeLogin(ctx context.Context, req *pb.UpdateNodeLoginRequest) (*pb.RPCUpdateSuccess, error) {
|
||||||
|
// 校验请求
|
||||||
|
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeAdmin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Login.Id <= 0 {
|
||||||
|
_, err := models.SharedNodeLoginDAO.CreateNodeLogin(req.NodeId, req.Login.Name, req.Login.Type, req.Login.Params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = models.SharedNodeLoginDAO.UpdateNodeLogin(req.Login.Id, req.Login.Name, req.Login.Type, req.Login.Params)
|
||||||
|
|
||||||
|
return rpcutils.RPCUpdateSuccess()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user