egress.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. package controller
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "net/http"
  7. "time"
  8. "github.com/google/uuid"
  9. "github.com/gorilla/mux"
  10. "github.com/gravitl/netmaker/db"
  11. "github.com/gravitl/netmaker/logger"
  12. "github.com/gravitl/netmaker/logic"
  13. "github.com/gravitl/netmaker/models"
  14. "github.com/gravitl/netmaker/mq"
  15. "github.com/gravitl/netmaker/schema"
  16. "gorm.io/datatypes"
  17. )
  18. func egressHandlers(r *mux.Router) {
  19. r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(createEgress))).Methods(http.MethodPost)
  20. r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(listEgress))).Methods(http.MethodGet)
  21. r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(updateEgress))).Methods(http.MethodPut)
  22. r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(deleteEgress))).Methods(http.MethodDelete)
  23. }
  24. // @Summary Create Egress Resource
  25. // @Router /api/v1/egress [post]
  26. // @Tags Auth
  27. // @Accept json
  28. // @Param body body models.Egress
  29. // @Success 200 {object} models.SuccessResponse
  30. // @Failure 400 {object} models.ErrorResponse
  31. // @Failure 401 {object} models.ErrorResponse
  32. // @Failure 500 {object} models.ErrorResponse
  33. func createEgress(w http.ResponseWriter, r *http.Request) {
  34. var req models.EgressReq
  35. err := json.NewDecoder(r.Body).Decode(&req)
  36. if err != nil {
  37. logger.Log(0, "error decoding request body: ",
  38. err.Error())
  39. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  40. return
  41. }
  42. var egressRange string
  43. if !req.IsInetGw {
  44. egressRange, err = logic.NormalizeCIDR(req.Range)
  45. if err != nil {
  46. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  47. return
  48. }
  49. } else {
  50. egressRange = "*"
  51. }
  52. e := schema.Egress{
  53. ID: uuid.New().String(),
  54. Name: req.Name,
  55. Network: req.Network,
  56. Description: req.Description,
  57. Range: egressRange,
  58. Nat: req.Nat,
  59. IsInetGw: req.IsInetGw,
  60. Nodes: make(datatypes.JSONMap),
  61. Tags: make(datatypes.JSONMap),
  62. Status: true,
  63. CreatedBy: r.Header.Get("user"),
  64. CreatedAt: time.Now().UTC(),
  65. }
  66. for nodeID, metric := range req.Nodes {
  67. e.Nodes[nodeID] = metric
  68. }
  69. if err := logic.ValidateEgressReq(&e); err != nil {
  70. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  71. return
  72. }
  73. err = e.Create(db.WithContext(r.Context()))
  74. if err != nil {
  75. logic.ReturnErrorResponse(
  76. w,
  77. r,
  78. logic.FormatError(errors.New("error creating egress resource"+err.Error()), "internal"),
  79. )
  80. return
  81. }
  82. if e.IsInetGw {
  83. for nodeID := range req.Nodes {
  84. node, err := logic.GetNodeByID(nodeID)
  85. if err == nil && !node.IsGw {
  86. node.IsGw = true
  87. node.IsIngressGateway = true
  88. node.IsRelay = true
  89. if node.Address.IP != nil {
  90. node.IngressDNS = node.Address.IP.String()
  91. } else {
  92. node.IngressDNS = node.Address6.IP.String()
  93. }
  94. logic.UpsertNode(&node)
  95. }
  96. }
  97. }
  98. logic.LogEvent(&models.Event{
  99. Action: models.Create,
  100. Source: models.Subject{
  101. ID: r.Header.Get("user"),
  102. Name: r.Header.Get("user"),
  103. Type: models.UserSub,
  104. },
  105. TriggeredBy: r.Header.Get("user"),
  106. Target: models.Subject{
  107. ID: e.ID,
  108. Name: e.Name,
  109. Type: models.EgressSub,
  110. },
  111. NetworkID: models.NetworkID(e.Network),
  112. Origin: models.Dashboard,
  113. })
  114. // for nodeID := range e.Nodes {
  115. // node, err := logic.GetNodeByID(nodeID)
  116. // if err != nil {
  117. // logic.AddEgressInfoToNode(&node, e)
  118. // logic.UpsertNode(&node)
  119. // }
  120. // }
  121. go mq.PublishPeerUpdate(false)
  122. logic.ReturnSuccessResponseWithJson(w, r, e, "created egress resource")
  123. }
  124. // @Summary List Egress Resource
  125. // @Router /api/v1/egress [get]
  126. // @Tags Auth
  127. // @Accept json
  128. // @Param query network string
  129. // @Success 200 {object} models.SuccessResponse
  130. // @Failure 400 {object} models.ErrorResponse
  131. // @Failure 401 {object} models.ErrorResponse
  132. // @Failure 500 {object} models.ErrorResponse
  133. func listEgress(w http.ResponseWriter, r *http.Request) {
  134. network := r.URL.Query().Get("network")
  135. if network == "" {
  136. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
  137. return
  138. }
  139. e := schema.Egress{Network: network}
  140. list, err := e.ListByNetwork(db.WithContext(r.Context()))
  141. if err != nil {
  142. logic.ReturnErrorResponse(
  143. w,
  144. r,
  145. logic.FormatError(errors.New("error listing egress resource"+err.Error()), "internal"),
  146. )
  147. return
  148. }
  149. logic.ReturnSuccessResponseWithJson(w, r, list, "fetched egress resource list")
  150. }
  151. // @Summary Update Egress Resource
  152. // @Router /api/v1/egress [put]
  153. // @Tags Auth
  154. // @Accept json
  155. // @Param body body models.Egress
  156. // @Success 200 {object} models.SuccessResponse
  157. // @Failure 400 {object} models.ErrorResponse
  158. // @Failure 401 {object} models.ErrorResponse
  159. // @Failure 500 {object} models.ErrorResponse
  160. func updateEgress(w http.ResponseWriter, r *http.Request) {
  161. var req models.EgressReq
  162. err := json.NewDecoder(r.Body).Decode(&req)
  163. if err != nil {
  164. logger.Log(0, "error decoding request body: ",
  165. err.Error())
  166. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  167. return
  168. }
  169. var egressRange string
  170. if !req.IsInetGw {
  171. egressRange, err = logic.NormalizeCIDR(req.Range)
  172. if err != nil {
  173. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  174. return
  175. }
  176. } else {
  177. egressRange = "*"
  178. }
  179. e := schema.Egress{ID: req.ID}
  180. err = e.Get(db.WithContext(r.Context()))
  181. if err != nil {
  182. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  183. return
  184. }
  185. var updateNat bool
  186. var updateInetGw bool
  187. var updateStatus bool
  188. if req.Nat != e.Nat {
  189. updateNat = true
  190. }
  191. if req.IsInetGw != e.IsInetGw {
  192. updateInetGw = true
  193. }
  194. if req.Status != e.Status {
  195. updateStatus = true
  196. }
  197. event := &models.Event{
  198. Action: models.Update,
  199. Source: models.Subject{
  200. ID: r.Header.Get("user"),
  201. Name: r.Header.Get("user"),
  202. Type: models.UserSub,
  203. },
  204. TriggeredBy: r.Header.Get("user"),
  205. Target: models.Subject{
  206. ID: e.ID,
  207. Name: e.Name,
  208. Type: models.EgressSub,
  209. },
  210. Diff: models.Diff{
  211. Old: e,
  212. },
  213. NetworkID: models.NetworkID(e.Network),
  214. Origin: models.Dashboard,
  215. }
  216. e.Nodes = make(datatypes.JSONMap)
  217. e.Tags = make(datatypes.JSONMap)
  218. for nodeID, metric := range req.Nodes {
  219. e.Nodes[nodeID] = metric
  220. }
  221. e.Range = egressRange
  222. e.Description = req.Description
  223. e.Name = req.Name
  224. e.Nat = req.Nat
  225. e.Status = req.Status
  226. e.IsInetGw = req.IsInetGw
  227. e.UpdatedAt = time.Now().UTC()
  228. if err := logic.ValidateEgressReq(&e); err != nil {
  229. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  230. return
  231. }
  232. err = e.Update(db.WithContext(context.TODO()))
  233. if err != nil {
  234. logic.ReturnErrorResponse(
  235. w,
  236. r,
  237. logic.FormatError(errors.New("error creating egress resource"+err.Error()), "internal"),
  238. )
  239. return
  240. }
  241. if updateNat {
  242. e.Nat = req.Nat
  243. e.UpdateNatStatus(db.WithContext(context.TODO()))
  244. }
  245. if updateInetGw {
  246. e.IsInetGw = req.IsInetGw
  247. e.UpdateINetGwStatus(db.WithContext(context.TODO()))
  248. }
  249. if updateStatus {
  250. e.Status = req.Status
  251. e.UpdateEgressStatus(db.WithContext(context.TODO()))
  252. }
  253. event.Diff.New = e
  254. logic.LogEvent(event)
  255. go mq.PublishPeerUpdate(false)
  256. logic.ReturnSuccessResponseWithJson(w, r, e, "updated egress resource")
  257. }
  258. // @Summary Delete Egress Resource
  259. // @Router /api/v1/egress [delete]
  260. // @Tags Auth
  261. // @Accept json
  262. // @Param body body models.Egress
  263. // @Success 200 {object} models.SuccessResponse
  264. // @Failure 400 {object} models.ErrorResponse
  265. // @Failure 401 {object} models.ErrorResponse
  266. // @Failure 500 {object} models.ErrorResponse
  267. func deleteEgress(w http.ResponseWriter, r *http.Request) {
  268. id := r.URL.Query().Get("id")
  269. if id == "" {
  270. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("id is required"), "badrequest"))
  271. return
  272. }
  273. e := schema.Egress{ID: id}
  274. err := e.Get(db.WithContext(r.Context()))
  275. if err != nil {
  276. logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
  277. return
  278. }
  279. err = e.Delete(db.WithContext(r.Context()))
  280. if err != nil {
  281. logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.Internal))
  282. return
  283. }
  284. logic.LogEvent(&models.Event{
  285. Action: models.Delete,
  286. Source: models.Subject{
  287. ID: r.Header.Get("user"),
  288. Name: r.Header.Get("user"),
  289. Type: models.UserSub,
  290. },
  291. TriggeredBy: r.Header.Get("user"),
  292. Target: models.Subject{
  293. ID: e.ID,
  294. Name: e.Name,
  295. Type: models.EgressSub,
  296. },
  297. NetworkID: models.NetworkID(e.Network),
  298. Origin: models.Dashboard,
  299. })
  300. // delete related acl policies
  301. acls := logic.ListAcls()
  302. for _, acl := range acls {
  303. for i := len(acl.Dst) - 1; i >= 0; i-- {
  304. if acl.Dst[i].ID == models.EgressID && acl.Dst[i].Value == id {
  305. acl.Dst = append(acl.Dst[:i], acl.Dst[i+1:]...)
  306. }
  307. }
  308. if len(acl.Dst) == 0 {
  309. logic.DeleteAcl(acl)
  310. } else {
  311. logic.UpsertAcl(acl)
  312. }
  313. }
  314. go mq.PublishPeerUpdate(false)
  315. logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted egress resource")
  316. }