tags.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. func UpsertTag(tag models.Tag) error {
  29. d, err := json.Marshal(tag)
  30. if err != nil {
  31. return err
  32. }
  33. return database.Insert(tag.ID.String(), string(d), database.TAG_TABLE_NAME)
  34. }
  35. // InsertTag - creates new tag
  36. func InsertTag(tag models.Tag) error {
  37. tagMutex.Lock()
  38. defer tagMutex.Unlock()
  39. _, err := database.FetchRecord(database.TAG_TABLE_NAME, tag.ID.String())
  40. if err == nil {
  41. return fmt.Errorf("tag `%s` exists already", tag.ID)
  42. }
  43. d, err := json.Marshal(tag)
  44. if err != nil {
  45. return err
  46. }
  47. return database.Insert(tag.ID.String(), string(d), database.TAG_TABLE_NAME)
  48. }
  49. // DeleteTag - delete tag, will also untag hosts
  50. func DeleteTag(tagID models.TagID, removeFromPolicy bool) error {
  51. tagMutex.Lock()
  52. defer tagMutex.Unlock()
  53. // cleanUp tags on hosts
  54. tag, err := GetTag(tagID)
  55. if err != nil {
  56. return err
  57. }
  58. nodes, err := GetNetworkNodes(tag.Network.String())
  59. if err != nil {
  60. return err
  61. }
  62. for _, nodeI := range nodes {
  63. nodeI := nodeI
  64. if _, ok := nodeI.Tags[tagID]; ok {
  65. delete(nodeI.Tags, tagID)
  66. UpsertNode(&nodeI)
  67. }
  68. }
  69. if removeFromPolicy {
  70. // remove tag used on acl policy
  71. go RemoveDeviceTagFromAclPolicies(tagID, tag.Network)
  72. }
  73. extclients, _ := GetNetworkExtClients(tag.Network.String())
  74. for _, extclient := range extclients {
  75. if _, ok := extclient.Tags[tagID]; ok {
  76. delete(extclient.Tags, tagID)
  77. SaveExtClient(&extclient)
  78. }
  79. }
  80. return database.DeleteRecord(database.TAG_TABLE_NAME, tagID.String())
  81. }
  82. // ListTagsWithHosts - lists all tags with tagged hosts
  83. func ListTagsWithNodes(netID models.NetworkID) ([]models.TagListResp, error) {
  84. tags, err := ListNetworkTags(netID)
  85. if err != nil {
  86. return []models.TagListResp{}, err
  87. }
  88. tagsNodeMap := GetTagMapWithNodesByNetwork(netID, true)
  89. resp := []models.TagListResp{}
  90. for _, tagI := range tags {
  91. tagRespI := models.TagListResp{
  92. Tag: tagI,
  93. UsedByCnt: len(tagsNodeMap[tagI.ID]),
  94. TaggedNodes: GetAllNodesAPI(tagsNodeMap[tagI.ID]),
  95. }
  96. resp = append(resp, tagRespI)
  97. }
  98. return resp, nil
  99. }
  100. // ListTags - lists all tags from DB
  101. func ListTags() ([]models.Tag, error) {
  102. tagMutex.RLock()
  103. defer tagMutex.RUnlock()
  104. data, err := database.FetchRecords(database.TAG_TABLE_NAME)
  105. if err != nil && !database.IsEmptyRecord(err) {
  106. return []models.Tag{}, err
  107. }
  108. tags := []models.Tag{}
  109. for _, dataI := range data {
  110. tag := models.Tag{}
  111. err := json.Unmarshal([]byte(dataI), &tag)
  112. if err != nil {
  113. continue
  114. }
  115. tags = append(tags, tag)
  116. }
  117. return tags, nil
  118. }
  119. // ListTags - lists all tags from DB
  120. func ListNetworkTags(netID models.NetworkID) ([]models.Tag, error) {
  121. tagMutex.RLock()
  122. defer tagMutex.RUnlock()
  123. data, err := database.FetchRecords(database.TAG_TABLE_NAME)
  124. if err != nil && !database.IsEmptyRecord(err) {
  125. return []models.Tag{}, err
  126. }
  127. tags := []models.Tag{}
  128. for _, dataI := range data {
  129. tag := models.Tag{}
  130. err := json.Unmarshal([]byte(dataI), &tag)
  131. if err != nil {
  132. continue
  133. }
  134. if tag.Network == netID {
  135. tags = append(tags, tag)
  136. }
  137. }
  138. return tags, nil
  139. }
  140. // UpdateTag - updates and syncs hosts with tag update
  141. func UpdateTag(req models.UpdateTagReq, newID models.TagID) {
  142. tagMutex.Lock()
  143. defer tagMutex.Unlock()
  144. var err error
  145. tagNodesMap := GetNodesWithTag(req.ID)
  146. for _, apiNode := range req.TaggedNodes {
  147. node := models.Node{}
  148. var nodeID string
  149. if apiNode.IsStatic {
  150. if apiNode.StaticNode.RemoteAccessClientID != "" {
  151. continue
  152. }
  153. extclient, err := GetExtClient(apiNode.StaticNode.ClientID, apiNode.StaticNode.Network)
  154. if err != nil {
  155. continue
  156. }
  157. node.IsStatic = true
  158. nodeID = extclient.ClientID
  159. node.StaticNode = extclient
  160. } else {
  161. node, err = GetNodeByID(apiNode.ID)
  162. if err != nil {
  163. continue
  164. }
  165. nodeID = node.ID.String()
  166. }
  167. if _, ok := tagNodesMap[nodeID]; !ok {
  168. if node.StaticNode.Tags == nil {
  169. node.StaticNode.Tags = make(map[models.TagID]struct{})
  170. }
  171. if node.Tags == nil {
  172. node.Tags = make(map[models.TagID]struct{})
  173. }
  174. if newID != "" {
  175. if node.IsStatic {
  176. node.StaticNode.Tags[newID] = struct{}{}
  177. SaveExtClient(&node.StaticNode)
  178. } else {
  179. node.Tags[newID] = struct{}{}
  180. UpsertNode(&node)
  181. }
  182. } else {
  183. if node.IsStatic {
  184. node.StaticNode.Tags[req.ID] = struct{}{}
  185. SaveExtClient(&node.StaticNode)
  186. } else {
  187. node.Tags[req.ID] = struct{}{}
  188. UpsertNode(&node)
  189. }
  190. }
  191. } else {
  192. if newID != "" {
  193. delete(node.Tags, req.ID)
  194. delete(node.StaticNode.Tags, req.ID)
  195. if node.IsStatic {
  196. node.StaticNode.Tags[newID] = struct{}{}
  197. SaveExtClient(&node.StaticNode)
  198. } else {
  199. node.Tags[newID] = struct{}{}
  200. UpsertNode(&node)
  201. }
  202. }
  203. delete(tagNodesMap, nodeID)
  204. }
  205. }
  206. for _, deletedTaggedNode := range tagNodesMap {
  207. delete(deletedTaggedNode.Tags, req.ID)
  208. delete(deletedTaggedNode.StaticNode.Tags, req.ID)
  209. if deletedTaggedNode.IsStatic {
  210. SaveExtClient(&deletedTaggedNode.StaticNode)
  211. } else {
  212. UpsertNode(&deletedTaggedNode)
  213. }
  214. }
  215. go func(req models.UpdateTagReq) {
  216. if newID != "" {
  217. tagNodesMap = GetNodesWithTag(req.ID)
  218. for _, nodeI := range tagNodesMap {
  219. nodeI := nodeI
  220. if nodeI.StaticNode.Tags == nil {
  221. nodeI.StaticNode.Tags = make(map[models.TagID]struct{})
  222. }
  223. if nodeI.Tags == nil {
  224. nodeI.Tags = make(map[models.TagID]struct{})
  225. }
  226. delete(nodeI.Tags, req.ID)
  227. delete(nodeI.StaticNode.Tags, req.ID)
  228. nodeI.Tags[newID] = struct{}{}
  229. nodeI.StaticNode.Tags[newID] = struct{}{}
  230. if nodeI.IsStatic {
  231. SaveExtClient(&nodeI.StaticNode)
  232. } else {
  233. UpsertNode(&nodeI)
  234. }
  235. }
  236. }
  237. }(req)
  238. }
  239. // SortTagEntrys - Sorts slice of Tag entries by their id
  240. func SortTagEntrys(tags []models.TagListResp) {
  241. sort.Slice(tags, func(i, j int) bool {
  242. return tags[i].ID < tags[j].ID
  243. })
  244. }
  245. func CheckIDSyntax(id string) error {
  246. if id == "" {
  247. return errors.New("name is required")
  248. }
  249. if len(id) < 3 {
  250. return errors.New("name should have min 3 characters")
  251. }
  252. reg, err := regexp.Compile("^[a-zA-Z0-9- ]+$")
  253. if err != nil {
  254. return err
  255. }
  256. if !reg.MatchString(id) {
  257. return errors.New("invalid name. allowed characters are [a-zA-Z-]")
  258. }
  259. return nil
  260. }
  261. func CreateDefaultTags(netID models.NetworkID) {
  262. // create tag for remote access gws in the network
  263. tag := models.Tag{
  264. ID: models.TagID(fmt.Sprintf("%s.%s", netID.String(), models.RemoteAccessTagName)),
  265. TagName: models.RemoteAccessTagName,
  266. Network: netID,
  267. CreatedBy: "auto",
  268. CreatedAt: time.Now(),
  269. }
  270. _, err := GetTag(tag.ID)
  271. if err == nil {
  272. return
  273. }
  274. err = InsertTag(tag)
  275. if err != nil {
  276. slog.Error("failed to create remote access gw tag", "error", err.Error())
  277. return
  278. }
  279. }