123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- package logic
- import (
- "encoding/json"
- "errors"
- "fmt"
- "regexp"
- "sort"
- "sync"
- "time"
- "github.com/gravitl/netmaker/database"
- "github.com/gravitl/netmaker/models"
- "golang.org/x/exp/slog"
- )
- var tagMutex = &sync.RWMutex{}
- // GetTag - fetches tag info
- func GetTag(tagID models.TagID) (models.Tag, error) {
- data, err := database.FetchRecord(database.TAG_TABLE_NAME, tagID.String())
- if err != nil {
- return models.Tag{}, err
- }
- tag := models.Tag{}
- err = json.Unmarshal([]byte(data), &tag)
- if err != nil {
- return tag, err
- }
- return tag, nil
- }
- // InsertTag - creates new tag
- func InsertTag(tag models.Tag) error {
- tagMutex.Lock()
- defer tagMutex.Unlock()
- _, err := database.FetchRecord(database.TAG_TABLE_NAME, tag.ID.String())
- if err == nil {
- return fmt.Errorf("tag `%s` exists already", tag.ID)
- }
- d, err := json.Marshal(tag)
- if err != nil {
- return err
- }
- return database.Insert(tag.ID.String(), string(d), database.TAG_TABLE_NAME)
- }
- // DeleteTag - delete tag, will also untag hosts
- func DeleteTag(tagID models.TagID, removeFromPolicy bool) error {
- tagMutex.Lock()
- defer tagMutex.Unlock()
- // cleanUp tags on hosts
- tag, err := GetTag(tagID)
- if err != nil {
- return err
- }
- nodes, err := GetNetworkNodes(tag.Network.String())
- if err != nil {
- return err
- }
- for _, nodeI := range nodes {
- nodeI := nodeI
- if _, ok := nodeI.Tags[tagID]; ok {
- delete(nodeI.Tags, tagID)
- UpsertNode(&nodeI)
- }
- }
- if removeFromPolicy {
- // remove tag used on acl policy
- go RemoveDeviceTagFromAclPolicies(tagID, tag.Network)
- }
- extclients, _ := GetNetworkExtClients(tag.Network.String())
- for _, extclient := range extclients {
- if _, ok := extclient.Tags[tagID]; ok {
- delete(extclient.Tags, tagID)
- SaveExtClient(&extclient)
- }
- }
- return database.DeleteRecord(database.TAG_TABLE_NAME, tagID.String())
- }
- // ListTagsWithHosts - lists all tags with tagged hosts
- func ListTagsWithNodes(netID models.NetworkID) ([]models.TagListResp, error) {
- tags, err := ListNetworkTags(netID)
- if err != nil {
- return []models.TagListResp{}, err
- }
- tagsNodeMap := GetTagMapWithNodesByNetwork(netID, true)
- resp := []models.TagListResp{}
- for _, tagI := range tags {
- tagRespI := models.TagListResp{
- Tag: tagI,
- UsedByCnt: len(tagsNodeMap[tagI.ID]),
- TaggedNodes: GetAllNodesAPI(tagsNodeMap[tagI.ID]),
- }
- resp = append(resp, tagRespI)
- }
- return resp, nil
- }
- // ListTags - lists all tags from DB
- func ListTags() ([]models.Tag, error) {
- tagMutex.RLock()
- defer tagMutex.RUnlock()
- data, err := database.FetchRecords(database.TAG_TABLE_NAME)
- if err != nil && !database.IsEmptyRecord(err) {
- return []models.Tag{}, err
- }
- tags := []models.Tag{}
- for _, dataI := range data {
- tag := models.Tag{}
- err := json.Unmarshal([]byte(dataI), &tag)
- if err != nil {
- continue
- }
- tags = append(tags, tag)
- }
- return tags, nil
- }
- // ListTags - lists all tags from DB
- func ListNetworkTags(netID models.NetworkID) ([]models.Tag, error) {
- tagMutex.RLock()
- defer tagMutex.RUnlock()
- data, err := database.FetchRecords(database.TAG_TABLE_NAME)
- if err != nil && !database.IsEmptyRecord(err) {
- return []models.Tag{}, err
- }
- tags := []models.Tag{}
- for _, dataI := range data {
- tag := models.Tag{}
- err := json.Unmarshal([]byte(dataI), &tag)
- if err != nil {
- continue
- }
- if tag.Network == netID {
- tags = append(tags, tag)
- }
- }
- return tags, nil
- }
- // UpdateTag - updates and syncs hosts with tag update
- func UpdateTag(req models.UpdateTagReq, newID models.TagID) {
- tagMutex.Lock()
- defer tagMutex.Unlock()
- var err error
- tagNodesMap := GetNodesWithTag(req.ID)
- for _, apiNode := range req.TaggedNodes {
- node := models.Node{}
- var nodeID string
- if apiNode.IsStatic {
- if apiNode.StaticNode.RemoteAccessClientID != "" {
- continue
- }
- extclient, err := GetExtClient(apiNode.StaticNode.ClientID, apiNode.StaticNode.Network)
- if err != nil {
- continue
- }
- node.IsStatic = true
- nodeID = extclient.ClientID
- node.StaticNode = extclient
- } else {
- node, err = GetNodeByID(apiNode.ID)
- if err != nil {
- continue
- }
- nodeID = node.ID.String()
- }
- if _, ok := tagNodesMap[nodeID]; !ok {
- if node.StaticNode.Tags == nil {
- node.StaticNode.Tags = make(map[models.TagID]struct{})
- }
- if node.Tags == nil {
- node.Tags = make(map[models.TagID]struct{})
- }
- if newID != "" {
- if node.IsStatic {
- node.StaticNode.Tags[newID] = struct{}{}
- SaveExtClient(&node.StaticNode)
- } else {
- node.Tags[newID] = struct{}{}
- UpsertNode(&node)
- }
- } else {
- if node.IsStatic {
- node.StaticNode.Tags[req.ID] = struct{}{}
- SaveExtClient(&node.StaticNode)
- } else {
- node.Tags[req.ID] = struct{}{}
- UpsertNode(&node)
- }
- }
- } else {
- if newID != "" {
- delete(node.Tags, req.ID)
- delete(node.StaticNode.Tags, req.ID)
- if node.IsStatic {
- node.StaticNode.Tags[newID] = struct{}{}
- SaveExtClient(&node.StaticNode)
- } else {
- node.Tags[newID] = struct{}{}
- UpsertNode(&node)
- }
- }
- delete(tagNodesMap, nodeID)
- }
- }
- for _, deletedTaggedNode := range tagNodesMap {
- delete(deletedTaggedNode.Tags, req.ID)
- delete(deletedTaggedNode.StaticNode.Tags, req.ID)
- if deletedTaggedNode.IsStatic {
- SaveExtClient(&deletedTaggedNode.StaticNode)
- } else {
- UpsertNode(&deletedTaggedNode)
- }
- }
- go func(req models.UpdateTagReq) {
- if newID != "" {
- tagNodesMap = GetNodesWithTag(req.ID)
- for _, nodeI := range tagNodesMap {
- nodeI := nodeI
- if nodeI.StaticNode.Tags == nil {
- nodeI.StaticNode.Tags = make(map[models.TagID]struct{})
- }
- if nodeI.Tags == nil {
- nodeI.Tags = make(map[models.TagID]struct{})
- }
- delete(nodeI.Tags, req.ID)
- delete(nodeI.StaticNode.Tags, req.ID)
- nodeI.Tags[newID] = struct{}{}
- nodeI.StaticNode.Tags[newID] = struct{}{}
- if nodeI.IsStatic {
- SaveExtClient(&nodeI.StaticNode)
- } else {
- UpsertNode(&nodeI)
- }
- }
- }
- }(req)
- }
- // SortTagEntrys - Sorts slice of Tag entries by their id
- func SortTagEntrys(tags []models.TagListResp) {
- sort.Slice(tags, func(i, j int) bool {
- return tags[i].ID < tags[j].ID
- })
- }
- func CheckIDSyntax(id string) error {
- if id == "" {
- return errors.New("name is required")
- }
- if len(id) < 3 {
- return errors.New("name should have min 3 characters")
- }
- reg, err := regexp.Compile("^[a-zA-Z0-9- ]+$")
- if err != nil {
- return err
- }
- if !reg.MatchString(id) {
- return errors.New("invalid name. allowed characters are [a-zA-Z-]")
- }
- return nil
- }
- func CreateDefaultTags(netID models.NetworkID) {
- // create tag for remote access gws in the network
- tag := models.Tag{
- ID: models.TagID(fmt.Sprintf("%s.%s", netID.String(), models.RemoteAccessTagName)),
- TagName: models.RemoteAccessTagName,
- Network: netID,
- CreatedBy: "auto",
- CreatedAt: time.Now(),
- }
- _, err := GetTag(tag.ID)
- if err == nil {
- return
- }
- err = InsertTag(tag)
- if err != nil {
- slog.Error("failed to create remote access gw tag", "error", err.Error())
- return
- }
- }
|