NET-1956: Async Node Status API (#3341)

* add node status api

* upsate node status api to return map data

* resolve merge conflicts
This commit is contained in:
Abhishek K 2025-02-24 08:48:24 +03:00 committed by GitHub
parent a805901a73
commit 48535f7ef1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 90 additions and 8 deletions

View file

@ -31,6 +31,7 @@ func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
r.HandleFunc("/api/v1/nodes/{network}/status", logic.SecurityCheck(true, http.HandlerFunc(getNetworkNodeStatus))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
}
@ -328,7 +329,7 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
}
nodes = logic.AddStaticNodestoList(nodes)
nodes = logic.AddStatusToNodes(nodes)
nodes = logic.AddStatusToNodes(nodes, false)
// returns all the nodes in JSON/API format
apiNodes := logic.GetAllNodesAPI(nodes[:])
logger.Log(2, r.Header.Get("user"), "fetched nodes on network", networkName)
@ -368,7 +369,7 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
}
nodes = logic.AddStaticNodestoList(nodes)
nodes = logic.AddStatusToNodes(nodes)
nodes = logic.AddStatusToNodes(nodes, false)
// return all the nodes in JSON/API format
apiNodes := logic.GetAllNodesAPI(nodes[:])
logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
@ -377,6 +378,52 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(apiNodes)
}
// @Summary Get all nodes status on the network
// @Router /api/v1/nodes/{network}/status [get]
// @Tags Nodes
// @Securitydefinitions.oauth2.application OAuth2Application
// @Success 200 {array} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
// Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
func getNetworkNodeStatus(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
netID := params["network"]
// validate network
_, err := logic.GetNetwork(netID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to get network %v", err), "badrequest"))
return
}
var nodes []models.Node
nodes, err = logic.GetNetworkNodes(netID)
if err != nil {
logger.Log(0, "error fetching all nodes info: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
username := r.Header.Get("user")
if r.Header.Get("ismaster") == "no" {
user, err := logic.GetUser(username)
if err != nil {
return
}
userPlatformRole, err := logic.GetRole(user.PlatformRoleID)
if err != nil {
return
}
if !userPlatformRole.FullAccess {
nodes = logic.GetFilteredNodesByUserAccess(*user, nodes)
}
}
nodes = logic.AddStaticNodestoList(nodes)
nodes = logic.AddStatusToNodes(nodes, false)
// return all the nodes in JSON/API format
apiNodesStatusMap := logic.GetNodesStatusAPI(nodes[:])
logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
logic.ReturnSuccessResponseWithJson(w, r, apiNodesStatusMap, "fetched nodes with metric status")
}
// @Summary Get an individual node
// @Router /api/nodes/{network}/{nodeid} [get]
// @Tags Nodes

View file

@ -443,7 +443,7 @@ func AddStaticNodestoList(nodes []models.Node) []models.Node {
return nodes
}
func AddStatusToNodes(nodes []models.Node) (nodesWithStatus []models.Node) {
func AddStatusToNodes(nodes []models.Node, statusCall bool) (nodesWithStatus []models.Node) {
aclDefaultPolicyStatusMap := make(map[string]bool)
for _, node := range nodes {
if _, ok := aclDefaultPolicyStatusMap[node.Network]; !ok {
@ -451,7 +451,12 @@ func AddStatusToNodes(nodes []models.Node) (nodesWithStatus []models.Node) {
defaultPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
aclDefaultPolicyStatusMap[node.Network] = defaultPolicy.Enabled
}
GetNodeStatus(&node, aclDefaultPolicyStatusMap[node.Network])
if statusCall {
GetNodeStatus(&node, aclDefaultPolicyStatusMap[node.Network])
} else {
GetNodeCheckInStatus(&node, true)
}
nodesWithStatus = append(nodesWithStatus, node)
}
return
@ -572,6 +577,16 @@ func GetAllNodesAPI(nodes []models.Node) []models.ApiNode {
return apiNodes[:]
}
// GetNodesStatusAPI - gets nodes status
func GetNodesStatusAPI(nodes []models.Node) map[string]models.ApiNodeStatus {
apiStatusNodesMap := make(map[string]models.ApiNodeStatus)
for i := range nodes {
newApiNode := nodes[i].ConvertToStatusNode()
apiStatusNodesMap[newApiNode.ID] = *newApiNode
}
return apiStatusNodesMap
}
// DeleteExpiredNodes - goroutine which deletes nodes which are expired
func DeleteExpiredNodes(ctx context.Context, peerUpdate chan *models.Node) {
// Delete Expired Nodes Every Hour

View file

@ -6,9 +6,9 @@ import (
"github.com/gravitl/netmaker/models"
)
var GetNodeStatus = getNodeStatus
var GetNodeStatus = GetNodeCheckInStatus
func getNodeStatus(node *models.Node, t bool) {
func GetNodeCheckInStatus(node *models.Node, t bool) {
// On CE check only last check-in time
if node.IsStatic {
if !node.StaticNode.Enabled {

View file

@ -8,6 +8,13 @@ import (
"golang.org/x/exp/slog"
)
type ApiNodeStatus struct {
ID string `json:"id"`
IsStatic bool `json:"is_static"`
IsUserNode bool `json:"is_user_node"`
Status NodeStatus `json:"status"`
}
// ApiNode is a stripped down Node DTO that exposes only required fields to external systems
type ApiNode struct {
ID string `json:"id,omitempty" validate:"required,min=5,id_unique"`
@ -132,6 +139,19 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
return &convertedNode
}
func (nm *Node) ConvertToStatusNode() *ApiNodeStatus {
apiNode := ApiNodeStatus{}
if nm.IsStatic {
apiNode.ID = nm.StaticNode.ClientID
} else {
apiNode.ID = nm.ID.String()
}
apiNode.IsStatic = nm.IsStatic
apiNode.IsUserNode = nm.IsUserNode
apiNode.Status = nm.Status
return &apiNode
}
// Node.ConvertToAPINode - converts a node to an API node
func (nm *Node) ConvertToAPINode() *ApiNode {
apiNode := ApiNode{}

View file

@ -1102,7 +1102,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
slog.Error("failed to get node network", "error", err)
continue
}
nodesWithStatus := logic.AddStatusToNodes([]models.Node{node})
nodesWithStatus := logic.AddStatusToNodes([]models.Node{node}, false)
if len(nodesWithStatus) > 0 {
node = nodesWithStatus[0]
}
@ -1143,7 +1143,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
if err != nil {
continue
}
nodesWithStatus := logic.AddStatusToNodes([]models.Node{node})
nodesWithStatus := logic.AddStatusToNodes([]models.Node{node}, false)
if len(nodesWithStatus) > 0 {
node = nodesWithStatus[0]
}