Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NET-1613: TAG Mgmt APIs #3161

Open
wants to merge 36 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d64f098
Tag CRUD APIs
abhishek9686 Sep 17, 2024
99220fd
fix update tag handler
abhishek9686 Sep 17, 2024
ee5d877
guard update tag with mutex
abhishek9686 Sep 18, 2024
3d392ee
add delete tag api
abhishek9686 Sep 18, 2024
6640b66
add tag to api host
abhishek9686 Sep 18, 2024
db2550b
add tag groups to enrollment key
abhishek9686 Sep 18, 2024
db224ba
allow tag name update
abhishek9686 Sep 19, 2024
c64dc85
associate tags to network level
abhishek9686 Sep 22, 2024
04b8737
move tags to node model
abhishek9686 Sep 22, 2024
7dffa98
associate enrollment key tags to node
abhishek9686 Sep 22, 2024
5e385c8
Merge pull request #3132 from gravitl/NET-1613-nodes
abhishek9686 Sep 22, 2024
ad4c663
fix tag udpate with new ID
abhishek9686 Sep 22, 2024
f1a1ab5
Merge pull request #3133 from gravitl/NET-1613-nodes
abhishek9686 Sep 22, 2024
6c10724
add mutex to tag operations
abhishek9686 Sep 22, 2024
d6351d2
Merge pull request #3134 from gravitl/NET-1615
abhishek9686 Sep 22, 2024
cc5ddd6
Merge branch 'develop' of https://github.com/gravitl/netmaker into NE…
abhishek9686 Sep 26, 2024
e521393
add validation checks on tag name
abhishek9686 Sep 28, 2024
9f921be
Merge branch 'NET-1613' of https://github.com/gravitl/netmaker into N…
abhishek9686 Sep 28, 2024
025167f
add regex check for tag name
abhishek9686 Sep 28, 2024
287bcd8
static node init
abhishek9686 Oct 2, 2024
b41353b
add static nodes to api resp
abhishek9686 Oct 14, 2024
6ccafe8
set static nodes to list
abhishek9686 Oct 15, 2024
9efdbcb
Merge branch 'develop' of https://github.com/gravitl/netmaker into NE…
abhishek9686 Oct 16, 2024
43d9489
fix static check
abhishek9686 Oct 16, 2024
272dfda
create default tags on network
abhishek9686 Oct 17, 2024
d407c6b
add node to remote access gw tag
abhishek9686 Oct 17, 2024
14152a9
add remote access tags in migration
abhishek9686 Oct 17, 2024
1596060
add tags to extclient
abhishek9686 Oct 17, 2024
6c1208a
resolve merge conflicts
abhishek9686 Oct 17, 2024
d2918cc
support tags system on ext clients
abhishek9686 Oct 17, 2024
618c11f
remove tag from extclient on tag deletion
abhishek9686 Oct 17, 2024
de7caba
update tags on extclient update call
abhishek9686 Oct 17, 2024
27f276a
isolate tag updates for extclient and nodes
abhishek9686 Oct 17, 2024
ffd9348
Merge pull request #3163 from gravitl/NET-1613-ext
abhishek9686 Oct 18, 2024
298749b
fix tag update on extclients
abhishek9686 Oct 18, 2024
d8a0398
add remoteaccess tag to extclients
abhishek9686 Oct 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions auth/host_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func SessionHandler(conn *websocket.Conn) {
if err = conn.WriteMessage(messageType, reponseData); err != nil {
logger.Log(0, "error during message writing:", err.Error())
}
go CheckNetRegAndHostUpdate(netsToAdd[:], &result.Host, uuid.Nil)
go CheckNetRegAndHostUpdate(netsToAdd[:], &result.Host, uuid.Nil, []models.TagID{})
case <-timeout: // the read from req.answerCh has timed out
logger.Log(0, "timeout signal recv,exiting oauth socket conn")
break
Expand All @@ -236,7 +236,7 @@ func SessionHandler(conn *websocket.Conn) {
}

// CheckNetRegAndHostUpdate - run through networks and send a host update
func CheckNetRegAndHostUpdate(networks []string, h *models.Host, relayNodeId uuid.UUID) {
func CheckNetRegAndHostUpdate(networks []string, h *models.Host, relayNodeId uuid.UUID, tags []models.TagID) {
// publish host update through MQ
for i := range networks {
network := networks[i]
Expand All @@ -246,6 +246,14 @@ func CheckNetRegAndHostUpdate(networks []string, h *models.Host, relayNodeId uui
logger.Log(0, "failed to add host to network:", h.ID.String(), h.Name, network, err.Error())
continue
}
if len(tags) > 0 {
newNode.Tags = make(map[models.TagID]struct{})
for _, tagI := range tags {
newNode.Tags[tagI] = struct{}{}
}
logic.UpsertNode(newNode)
}

if relayNodeId != uuid.Nil && !newNode.IsRelayed {
// check if relay node exists and acting as relay
relaynode, err := logic.GetNodeByID(relayNodeId.String())
Expand Down
1 change: 1 addition & 0 deletions controllers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var HttpHandlers = []interface{}{
loggerHandlers,
hostHandlers,
enrollmentKeyHandlers,
tagHandlers,
legacyHandlers,
}

Expand Down
6 changes: 4 additions & 2 deletions controllers/enrollmentkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
newTime,
enrollmentKeyBody.Networks,
enrollmentKeyBody.Tags,
enrollmentKeyBody.Groups,
enrollmentKeyBody.Unlimited,
relayId,
)
Expand Down Expand Up @@ -206,7 +207,7 @@ func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
}
}

newEnrollmentKey, err := logic.UpdateEnrollmentKey(keyId, relayId)
newEnrollmentKey, err := logic.UpdateEnrollmentKey(keyId, relayId, enrollmentKeyBody.Groups)
if err != nil {
slog.Error("failed to update enrollment key", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
Expand Down Expand Up @@ -307,6 +308,7 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
return
}
}

if err = logic.CreateHost(&newHost); err != nil {
logger.Log(
0,
Expand Down Expand Up @@ -355,5 +357,5 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(&response)
// notify host of changes, peer and node updates
go auth.CheckNetRegAndHostUpdate(enrollmentKey.Networks, &newHost, enrollmentKey.Relay)
go auth.CheckNetRegAndHostUpdate(enrollmentKey.Networks, &newHost, enrollmentKey.Relay, enrollmentKey.Groups)
}
3 changes: 2 additions & 1 deletion controllers/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,9 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID))

logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID))
logic.CreateDefaultTags(models.NetworkID(network.NetID))
//add new network to allocated ip map
go logic.AddNetworkToAllocatedIpMap(network.NetID)

Expand Down
3 changes: 3 additions & 0 deletions controllers/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
if len(filteredNodes) > 0 {
nodes = filteredNodes
}
nodes = logic.AddStaticNodestoList(nodes)

// returns all the nodes in JSON/API format
apiNodes := logic.GetAllNodesAPI(nodes[:])
Expand Down Expand Up @@ -363,7 +364,9 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
if !userPlatformRole.FullAccess {
nodes = logic.GetFilteredNodesByUserAccess(*user, nodes)
}

}
nodes = logic.AddStaticNodestoList(nodes)
// 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")
Expand Down
199 changes: 199 additions & 0 deletions controllers/tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package controller

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"

"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
)

func tagHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/tags", logic.SecurityCheck(true, http.HandlerFunc(getTags))).
Methods(http.MethodGet)
r.HandleFunc("/api/v1/tags", logic.SecurityCheck(true, http.HandlerFunc(createTag))).
Methods(http.MethodPost)
r.HandleFunc("/api/v1/tags", logic.SecurityCheck(true, http.HandlerFunc(updateTag))).
Methods(http.MethodPut)
r.HandleFunc("/api/v1/tags", logic.SecurityCheck(true, http.HandlerFunc(deleteTag))).
Methods(http.MethodDelete)

}

// @Summary List Tags in a network
// @Router /api/v1/tags [get]
// @Tags TAG
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func getTags(w http.ResponseWriter, r *http.Request) {
netID, _ := url.QueryUnescape(r.URL.Query().Get("network"))
if netID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network id param is missing"), "badrequest"))
return
}
// check if network exists
_, err := logic.GetNetwork(netID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
tags, err := logic.ListTagsWithNodes(models.NetworkID(netID))
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to get all network tag entries: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
logic.SortTagEntrys(tags[:])
logic.ReturnSuccessResponseWithJson(w, r, tags, "fetched all tags in the network "+netID)
}

// @Summary Create Tag
// @Router /api/v1/tags [post]
// @Tags TAG
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func createTag(w http.ResponseWriter, r *http.Request) {
var req models.CreateTagReq
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logger.Log(0, "error decoding request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
user, err := logic.GetUser(r.Header.Get("user"))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
// check if tag network exists
_, err = logic.GetNetwork(req.Network.String())
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to get network details for "+req.Network.String()), "badrequest"))
return
}
// check if tag exists
tag := models.Tag{
ID: models.TagID(fmt.Sprintf("%s.%s", req.Network, req.TagName)),
TagName: req.TagName,
Network: req.Network,
CreatedBy: user.UserName,
CreatedAt: time.Now(),
}
_, err = logic.GetTag(tag.ID)
if err == nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("tag with id %s exists already", tag.TagName), "badrequest"))
return
}
// validate name
err = logic.CheckIDSyntax(tag.TagName)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
err = logic.InsertTag(tag)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
go func() {
for _, node := range req.TaggedNodes {
if node.IsStatic {
extclient, err := logic.GetExtClient(node.StaticNode.ClientID, node.StaticNode.Network)
if err == nil && extclient.RemoteAccessClientID == "" {
if extclient.Tags == nil {
extclient.Tags = make(map[models.TagID]struct{})
}
extclient.Tags[tag.ID] = struct{}{}
logic.SaveExtClient(&extclient)
}
continue
}
node, err := logic.GetNodeByID(node.ID)
if err != nil {
continue
}
if node.Tags == nil {
node.Tags = make(map[models.TagID]struct{})
}
node.Tags[tag.ID] = struct{}{}
logic.UpsertNode(&node)
}
}()

logic.ReturnSuccessResponseWithJson(w, r, req, "created tag successfully")
}

// @Summary Update Tag
// @Router /api/v1/tags [put]
// @Tags TAG
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func updateTag(w http.ResponseWriter, r *http.Request) {
var updateTag models.UpdateTagReq
err := json.NewDecoder(r.Body).Decode(&updateTag)
if err != nil {
logger.Log(0, "error decoding request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}

tag, err := logic.GetTag(updateTag.ID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
updateTag.NewName = strings.TrimSpace(updateTag.NewName)
var newID models.TagID
if updateTag.NewName != "" {
// validate name
err = logic.CheckIDSyntax(updateTag.NewName)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
newID = models.TagID(fmt.Sprintf("%s.%s", tag.Network, updateTag.NewName))
tag.ID = newID
tag.TagName = updateTag.NewName
err = logic.InsertTag(tag)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
// delete old Tag entry
logic.DeleteTag(updateTag.ID)
}
go logic.UpdateTag(updateTag, newID)
logic.ReturnSuccessResponse(w, r, "updating tags")
}

// @Summary Delete Tag
// @Router /api/v1/tags [delete]
// @Tags TAG
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func deleteTag(w http.ResponseWriter, r *http.Request) {
tagID, _ := url.QueryUnescape(r.URL.Query().Get("tag_id"))
if tagID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
return
}
err := logic.DeleteTag(models.TagID(tagID))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
logic.ReturnSuccessResponse(w, r, "deleted tag "+tagID)
}
3 changes: 3 additions & 0 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ const (
PENDING_USERS_TABLE_NAME = "pending_users"
// USER_INVITES - table for user invites
USER_INVITES_TABLE_NAME = "user_invites"
// TAG_TABLE_NAME - table for tags
TAG_TABLE_NAME = "tags"
// == ERROR CONSTS ==
// NO_RECORD - no singular result found
NO_RECORD = "no result found"
Expand Down Expand Up @@ -152,6 +154,7 @@ func createTables() {
CreateTable(PENDING_USERS_TABLE_NAME)
CreateTable(USER_PERMISSIONS_TABLE_NAME)
CreateTable(USER_INVITES_TABLE_NAME)
CreateTable(TAG_TABLE_NAME)
}

func CreateTable(tableName string) error {
Expand Down
7 changes: 4 additions & 3 deletions logic/enrollmentkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
)

// CreateEnrollmentKey - creates a new enrollment key in db
func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, unlimited bool, relay uuid.UUID) (*models.EnrollmentKey, error) {
func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, groups []models.TagID, unlimited bool, relay uuid.UUID) (*models.EnrollmentKey, error) {
newKeyID, err := getUniqueEnrollmentID()
if err != nil {
return nil, err
Expand All @@ -51,6 +51,7 @@ func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string
Tags: []string{},
Type: models.Undefined,
Relay: relay,
Groups: groups,
}
if uses > 0 {
k.UsesRemaining = uses
Expand Down Expand Up @@ -89,7 +90,7 @@ func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string
}

// UpdateEnrollmentKey - updates an existing enrollment key's associated relay
func UpdateEnrollmentKey(keyId string, relayId uuid.UUID) (*models.EnrollmentKey, error) {
func UpdateEnrollmentKey(keyId string, relayId uuid.UUID, groups []models.TagID) (*models.EnrollmentKey, error) {
key, err := GetEnrollmentKey(keyId)
if err != nil {
return nil, err
Expand All @@ -109,7 +110,7 @@ func UpdateEnrollmentKey(keyId string, relayId uuid.UUID) (*models.EnrollmentKey
}

key.Relay = relayId

key.Groups = groups
if err = upsertEnrollmentKey(&key); err != nil {
return nil, err
}
Expand Down
Loading
Loading