tags.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package logic
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "regexp"
  7. "sort"
  8. "sync"
  9. "time"
  10. "github.com/gravitl/netmaker/database"
  11. "github.com/gravitl/netmaker/models"
  12. "golang.org/x/exp/slog"
  13. )
  14. var tagMutex = &sync.RWMutex{}
  15. // GetTag - fetches tag info
  16. func GetTag(tagID models.TagID) (models.Tag, error) {
  17. data, err := database.FetchRecord(database.TAG_TABLE_NAME, tagID.String())
  18. if err != nil {
  19. return models.Tag{}, err
  20. }
  21. tag := models.Tag{}
  22. err = json.Unmarshal([]byte(data), &tag)
  23. if err != nil {
  24. return tag, err
  25. }
  26. return tag, nil
  27. }
  28. // InsertTag - creates new tag
  29. func InsertTag(tag models.Tag) error {
  30. tagMutex.Lock()
  31. defer tagMutex.Unlock()
  32. _, err := database.FetchRecord(database.TAG_TABLE_NAME, tag.ID.String())
  33. if err == nil {
  34. return fmt.Errorf("tag `%s` exists already", tag.ID)
  35. }
  36. d, err := json.Marshal(tag)
  37. if err != nil {
  38. return err
  39. }
  40. return database.Insert(tag.ID.String(), string(d), database.TAG_TABLE_NAME)
  41. }
  42. // DeleteTag - delete tag, will also untag hosts
  43. func DeleteTag(tagID models.TagID, removeFromPolicy bool) error {
  44. tagMutex.Lock()
  45. defer tagMutex.Unlock()
  46. // cleanUp tags on hosts
  47. tag, err := GetTag(tagID)
  48. if err != nil {
  49. return err
  50. }
  51. nodes, err := GetNetworkNodes(tag.Network.String())
  52. if err != nil {
  53. return err
  54. }
  55. for _, nodeI := range nodes {
  56. nodeI := nodeI
  57. if _, ok := nodeI.Tags[tagID]; ok {
  58. delete(nodeI.Tags, tagID)
  59. UpsertNode(&nodeI)
  60. }
  61. }
  62. if removeFromPolicy {
  63. // remove tag used on acl policy
  64. go RemoveDeviceTagFromAclPolicies(tagID, tag.Network)
  65. }
  66. return database.DeleteRecord(database.TAG_TABLE_NAME, tagID.String())
  67. }
  68. // ListTagsWithHosts - lists all tags with tagged hosts
  69. func ListTagsWithNodes(netID models.NetworkID) ([]models.TagListResp, error) {
  70. tags, err := ListNetworkTags(netID)
  71. if err != nil {
  72. return []models.TagListResp{}, err
  73. }
  74. tagsNodeMap := GetTagMapWithNodesByNetwork(netID)
  75. resp := []models.TagListResp{}
  76. for _, tagI := range tags {
  77. tagRespI := models.TagListResp{
  78. Tag: tagI,
  79. UsedByCnt: len(tagsNodeMap[tagI.ID]),
  80. TaggedNodes: tagsNodeMap[tagI.ID],
  81. }
  82. resp = append(resp, tagRespI)
  83. }
  84. return resp, nil
  85. }
  86. // ListTags - lists all tags from DB
  87. func ListTags() ([]models.Tag, error) {
  88. tagMutex.RLock()
  89. defer tagMutex.RUnlock()
  90. data, err := database.FetchRecords(database.TAG_TABLE_NAME)
  91. if err != nil && !database.IsEmptyRecord(err) {
  92. return []models.Tag{}, err
  93. }
  94. tags := []models.Tag{}
  95. for _, dataI := range data {
  96. tag := models.Tag{}
  97. err := json.Unmarshal([]byte(dataI), &tag)
  98. if err != nil {
  99. continue
  100. }
  101. tags = append(tags, tag)
  102. }
  103. return tags, nil
  104. }
  105. // ListTags - lists all tags from DB
  106. func ListNetworkTags(netID models.NetworkID) ([]models.Tag, error) {
  107. tagMutex.RLock()
  108. defer tagMutex.RUnlock()
  109. data, err := database.FetchRecords(database.TAG_TABLE_NAME)
  110. if err != nil && !database.IsEmptyRecord(err) {
  111. return []models.Tag{}, err
  112. }
  113. tags := []models.Tag{}
  114. for _, dataI := range data {
  115. tag := models.Tag{}
  116. err := json.Unmarshal([]byte(dataI), &tag)
  117. if err != nil {
  118. continue
  119. }
  120. if tag.Network == netID {
  121. tags = append(tags, tag)
  122. }
  123. }
  124. return tags, nil
  125. }
  126. // UpdateTag - updates and syncs hosts with tag update
  127. func UpdateTag(req models.UpdateTagReq, newID models.TagID) {
  128. tagMutex.Lock()
  129. defer tagMutex.Unlock()
  130. tagNodesMap := GetNodesWithTag(req.ID)
  131. for _, nodeID := range req.TaggedNodes {
  132. node, err := GetNodeByID(nodeID)
  133. if err != nil {
  134. continue
  135. }
  136. if _, ok := tagNodesMap[node.ID.String()]; !ok {
  137. if node.Tags == nil {
  138. node.Tags = make(map[models.TagID]struct{})
  139. }
  140. if newID != "" {
  141. node.Tags[newID] = struct{}{}
  142. } else {
  143. node.Tags[req.ID] = struct{}{}
  144. }
  145. UpsertNode(&node)
  146. } else {
  147. if newID != "" {
  148. delete(node.Tags, req.ID)
  149. node.Tags[newID] = struct{}{}
  150. UpsertNode(&node)
  151. }
  152. delete(tagNodesMap, node.ID.String())
  153. }
  154. }
  155. for _, deletedTaggedNode := range tagNodesMap {
  156. deletedTaggedHost := deletedTaggedNode
  157. delete(deletedTaggedHost.Tags, req.ID)
  158. UpsertNode(&deletedTaggedHost)
  159. }
  160. go func(req models.UpdateTagReq) {
  161. if newID != "" {
  162. tagNodesMap = GetNodesWithTag(req.ID)
  163. for _, nodeI := range tagNodesMap {
  164. nodeI := nodeI
  165. delete(nodeI.Tags, req.ID)
  166. nodeI.Tags[newID] = struct{}{}
  167. UpsertNode(&nodeI)
  168. }
  169. }
  170. }(req)
  171. }
  172. // SortTagEntrys - Sorts slice of Tag entries by their id
  173. func SortTagEntrys(tags []models.TagListResp) {
  174. sort.Slice(tags, func(i, j int) bool {
  175. return tags[i].ID < tags[j].ID
  176. })
  177. }
  178. func CheckIDSyntax(id string) error {
  179. if id == "" {
  180. return errors.New("name is required")
  181. }
  182. if len(id) < 3 {
  183. return errors.New("name should have min 3 characters")
  184. }
  185. reg, err := regexp.Compile("^[a-zA-Z-]+$")
  186. if err != nil {
  187. return err
  188. }
  189. if !reg.MatchString(id) {
  190. return errors.New("invalid name. allowed characters are [a-zA-Z-]")
  191. }
  192. return nil
  193. }
  194. func CreateDefaultTags(netID models.NetworkID) {
  195. // create tag for remote access gws in the network
  196. tag := models.Tag{
  197. ID: models.TagID(fmt.Sprintf("%s.%s", netID.String(), "remote-access-gws")),
  198. TagName: "remote-access-gws",
  199. Network: netID,
  200. CreatedBy: "auto",
  201. CreatedAt: time.Now(),
  202. }
  203. _, err := GetTag(tag.ID)
  204. if err == nil {
  205. return
  206. }
  207. err = InsertTag(tag)
  208. if err != nil {
  209. slog.Error("failed to create remote access gw tag", "error", err.Error())
  210. return
  211. }
  212. }