tags.go 7.0 KB

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