可以在集群中查看待安装节点、并直接安装节点

This commit is contained in:
GoEdgeLab
2020-10-26 21:14:56 +08:00
parent b286baf12e
commit 8444e9b312
11 changed files with 246 additions and 15 deletions

View File

@@ -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

View File

@@ -1 +1 @@
installer-* edge-*

View File

@@ -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().

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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"` // 步骤
} }

View 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,
}
}

View File

@@ -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{

View File

@@ -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 {

View File

@@ -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
} }

View File

@@ -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()
}