failover.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. package controllers
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "github.com/google/uuid"
  8. "github.com/gorilla/mux"
  9. controller "github.com/gravitl/netmaker/controllers"
  10. "github.com/gravitl/netmaker/logger"
  11. "github.com/gravitl/netmaker/logic"
  12. "github.com/gravitl/netmaker/models"
  13. "github.com/gravitl/netmaker/mq"
  14. proLogic "github.com/gravitl/netmaker/pro/logic"
  15. "golang.org/x/exp/slog"
  16. )
  17. // FailOverHandlers - handlers for FailOver
  18. func FailOverHandlers(r *mux.Router) {
  19. r.HandleFunc("/api/v1/node/{nodeid}/failover", http.HandlerFunc(getfailOver)).
  20. Methods(http.MethodGet)
  21. r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(createfailOver))).
  22. Methods(http.MethodPost)
  23. r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(deletefailOver))).
  24. Methods(http.MethodDelete)
  25. r.HandleFunc("/api/v1/node/{network}/failover/reset", logic.SecurityCheck(true, http.HandlerFunc(resetFailOver))).
  26. Methods(http.MethodPost)
  27. r.HandleFunc("/api/v1/node/{nodeid}/failover_me", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).
  28. Methods(http.MethodPost)
  29. r.HandleFunc("/api/v1/node/{nodeid}/register_ack/peer/{peerid}",
  30. controller.Authorize(true, false, "host", http.HandlerFunc(registerFailoverAck))).
  31. Methods(http.MethodPost)
  32. }
  33. // @Summary Get failover node
  34. // @Router /api/v1/node/{nodeid}/failover [get]
  35. // @Tags PRO
  36. // @Param nodeid path string true "Node ID"
  37. // @Success 200 {object} models.Node
  38. // @Failure 400 {object} models.ErrorResponse
  39. // @Failure 404 {object} models.ErrorResponse
  40. func getfailOver(w http.ResponseWriter, r *http.Request) {
  41. var params = mux.Vars(r)
  42. nodeid := params["nodeid"]
  43. // confirm host exists
  44. node, err := logic.GetNodeByID(nodeid)
  45. if err != nil {
  46. slog.Error("failed to get node:", "node", nodeid, "error", err.Error())
  47. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  48. return
  49. }
  50. failOverNode, exists := proLogic.FailOverExists(node.Network)
  51. if !exists {
  52. logic.ReturnErrorResponse(
  53. w,
  54. r,
  55. logic.FormatError(errors.New("failover node not found"), "notfound"),
  56. )
  57. return
  58. }
  59. w.Header().Set("Content-Type", "application/json")
  60. logic.ReturnSuccessResponseWithJson(w, r, failOverNode, "get failover node successfully")
  61. }
  62. // @Summary Create failover node
  63. // @Router /api/v1/node/{nodeid}/failover [post]
  64. // @Tags PRO
  65. // @Param nodeid path string true "Node ID"
  66. // @Success 200 {object} models.Node
  67. // @Failure 400 {object} models.ErrorResponse
  68. // @Failure 500 {object} models.ErrorResponse
  69. func createfailOver(w http.ResponseWriter, r *http.Request) {
  70. var params = mux.Vars(r)
  71. nodeid := params["nodeid"]
  72. // confirm host exists
  73. node, err := logic.GetNodeByID(nodeid)
  74. if err != nil {
  75. slog.Error("failed to get node:", "error", err.Error())
  76. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  77. return
  78. }
  79. err = proLogic.CreateFailOver(node)
  80. if err != nil {
  81. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  82. return
  83. }
  84. go mq.PublishPeerUpdate(false)
  85. w.Header().Set("Content-Type", "application/json")
  86. logic.ReturnSuccessResponseWithJson(w, r, node, "created failover successfully")
  87. }
  88. // @Summary Reset failover for a network
  89. // @Router /api/v1/node/{network}/failover/reset [post]
  90. // @Tags PRO
  91. // @Param network path string true "Network ID"
  92. // @Success 200 {object} models.SuccessResponse
  93. // @Failure 500 {object} models.ErrorResponse
  94. func resetFailOver(w http.ResponseWriter, r *http.Request) {
  95. var params = mux.Vars(r)
  96. net := params["network"]
  97. nodes, err := logic.GetNetworkNodes(net)
  98. if err != nil {
  99. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  100. return
  101. }
  102. for _, node := range nodes {
  103. if node.FailedOverBy != uuid.Nil {
  104. node.FailedOverBy = uuid.Nil
  105. node.FailOverPeers = make(map[string]struct{})
  106. logic.UpsertNode(&node)
  107. }
  108. }
  109. go mq.PublishPeerUpdate(false)
  110. w.Header().Set("Content-Type", "application/json")
  111. logic.ReturnSuccessResponse(w, r, "failover has been reset successfully")
  112. }
  113. // @Summary Delete failover node
  114. // @Router /api/v1/node/{nodeid}/failover [delete]
  115. // @Tags PRO
  116. // @Param nodeid path string true "Node ID"
  117. // @Success 200 {object} models.Node
  118. // @Failure 400 {object} models.ErrorResponse
  119. // @Failure 500 {object} models.ErrorResponse
  120. func deletefailOver(w http.ResponseWriter, r *http.Request) {
  121. var params = mux.Vars(r)
  122. nodeid := params["nodeid"]
  123. // confirm host exists
  124. node, err := logic.GetNodeByID(nodeid)
  125. if err != nil {
  126. slog.Error("failed to get node:", "error", err.Error())
  127. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  128. return
  129. }
  130. node.IsFailOver = false
  131. // Reset FailOvered Peers
  132. err = logic.UpsertNode(&node)
  133. if err != nil {
  134. slog.Error("failed to upsert node", "node", node.ID.String(), "error", err)
  135. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  136. return
  137. }
  138. // delete network failover from cache
  139. proLogic.DeleteFailOverFromCache(node.Network)
  140. go func() {
  141. proLogic.ResetFailOver(&node)
  142. mq.PublishPeerUpdate(false)
  143. }()
  144. w.Header().Set("Content-Type", "application/json")
  145. logic.ReturnSuccessResponseWithJson(w, r, node, "deleted failover successfully")
  146. }
  147. // @Summary Failover me
  148. // @Router /api/v1/node/{nodeid}/failover_me [post]
  149. // @Tags PRO
  150. // @Param nodeid path string true "Node ID"
  151. // @Accept json
  152. // @Param body body models.FailOverMeReq true "Failover request"
  153. // @Success 200 {object} models.SuccessResponse
  154. // @Failure 400 {object} models.ErrorResponse
  155. // @Failure 500 {object} models.ErrorResponse
  156. func failOverME(w http.ResponseWriter, r *http.Request) {
  157. var params = mux.Vars(r)
  158. nodeid := params["nodeid"]
  159. // confirm host exists
  160. node, err := logic.GetNodeByID(nodeid)
  161. if err != nil {
  162. logger.Log(0, r.Header.Get("user"), "failed to get node:", err.Error())
  163. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  164. return
  165. }
  166. host, err := logic.GetHost(node.HostID.String())
  167. if err != nil {
  168. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  169. return
  170. }
  171. failOverNode, exists := proLogic.FailOverExists(node.Network)
  172. if !exists {
  173. logic.ReturnErrorResponse(
  174. w,
  175. r,
  176. logic.FormatError(
  177. fmt.Errorf("req-from: %s, failover node doesn't exist in the network", host.Name),
  178. "badrequest",
  179. ),
  180. )
  181. return
  182. }
  183. var failOverReq models.FailOverMeReq
  184. err = json.NewDecoder(r.Body).Decode(&failOverReq)
  185. if err != nil {
  186. logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
  187. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  188. return
  189. }
  190. var sendPeerUpdate bool
  191. peerNode, err := logic.GetNodeByID(failOverReq.NodeID)
  192. if err != nil {
  193. slog.Error("peer not found: ", "nodeid", failOverReq.NodeID, "error", err)
  194. logic.ReturnErrorResponse(
  195. w,
  196. r,
  197. logic.FormatError(errors.New("peer not found"), "badrequest"),
  198. )
  199. return
  200. }
  201. if peerNode.IsFailOver {
  202. logic.ReturnErrorResponse(
  203. w,
  204. r,
  205. logic.FormatError(errors.New("peer is acting as failover"), "badrequest"),
  206. )
  207. return
  208. }
  209. if node.IsFailOver {
  210. logic.ReturnErrorResponse(
  211. w,
  212. r,
  213. logic.FormatError(errors.New("node is acting as failover"), "badrequest"),
  214. )
  215. return
  216. }
  217. if peerNode.IsFailOver {
  218. logic.ReturnErrorResponse(
  219. w,
  220. r,
  221. logic.FormatError(errors.New("peer is acting as failover"), "badrequest"),
  222. )
  223. return
  224. }
  225. if node.IsRelayed && node.RelayedBy == peerNode.ID.String() {
  226. logic.ReturnErrorResponse(
  227. w,
  228. r,
  229. logic.FormatError(errors.New("node is relayed by peer node"), "badrequest"),
  230. )
  231. return
  232. }
  233. if node.IsRelay && peerNode.RelayedBy == node.ID.String() {
  234. logic.ReturnErrorResponse(
  235. w,
  236. r,
  237. logic.FormatError(errors.New("node acting as relay for the peer node"), "badrequest"),
  238. )
  239. return
  240. }
  241. if node.IsInternetGateway && peerNode.InternetGwID == node.ID.String() {
  242. logic.ReturnErrorResponse(
  243. w,
  244. r,
  245. logic.FormatError(
  246. errors.New("node acting as internet gw for the peer node"),
  247. "badrequest",
  248. ),
  249. )
  250. return
  251. }
  252. if node.InternetGwID != "" && node.InternetGwID == peerNode.ID.String() {
  253. logic.ReturnErrorResponse(
  254. w,
  255. r,
  256. logic.FormatError(
  257. errors.New("node using a internet gw by the peer node"),
  258. "badrequest",
  259. ),
  260. )
  261. return
  262. }
  263. err = proLogic.SetFailOverCtx(failOverNode, node, peerNode)
  264. if err != nil {
  265. slog.Error("failed to create failover", "id", node.ID.String(),
  266. "network", node.Network, "error", err)
  267. logic.ReturnErrorResponse(
  268. w,
  269. r,
  270. logic.FormatError(fmt.Errorf("failed to create failover: %v", err), "internal"),
  271. )
  272. return
  273. }
  274. slog.Info(
  275. "[auto-relay] created relay on node",
  276. "node",
  277. node.ID.String(),
  278. "network",
  279. node.Network,
  280. )
  281. sendPeerUpdate = true
  282. if sendPeerUpdate {
  283. go mq.PublishPeerUpdate(false)
  284. }
  285. w.Header().Set("Content-Type", "application/json")
  286. logic.ReturnSuccessResponse(w, r, "relayed successfully")
  287. }
  288. // @Summary Register Failover Ack
  289. // @Router /api/v1/node/{nodeid}/register_ack/peer/{peerid} [post]
  290. // @Tags PRO
  291. // @Param nodeid path string true "Node ID"
  292. // @Param peerid path string true "Peer ID"
  293. // @Accept json
  294. // @Param body body models.FailOverMeReq true "Failover request"
  295. // @Success 200 {object} models.SuccessResponse
  296. // @Failure 400 {object} models.ErrorResponse
  297. // @Failure 500 {object} models.ErrorResponse
  298. func registerFailoverAck(w http.ResponseWriter, r *http.Request) {
  299. var params = mux.Vars(r)
  300. nodeid := params["nodeid"]
  301. peerid := params["peerid"]
  302. // confirm node exists
  303. node, err := logic.GetNodeByID(nodeid)
  304. if err != nil {
  305. logger.Log(0, r.Header.Get("user"), "failed to get node:", err.Error())
  306. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  307. return
  308. }
  309. failOverNode, exists := proLogic.FailOverExists(node.Network)
  310. if !exists {
  311. logic.ReturnErrorResponse(
  312. w,
  313. r,
  314. logic.FormatError(
  315. fmt.Errorf("failover node doesn't exist in the network %s", node.Network),
  316. "badrequest",
  317. ),
  318. )
  319. return
  320. }
  321. // confirm peer exists
  322. peer, err := logic.GetNodeByID(peerid)
  323. if err != nil {
  324. logger.Log(0, r.Header.Get("user"), "failed to get node:", err.Error())
  325. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  326. return
  327. }
  328. var sendPeerUpdate bool
  329. if peer.IsFailOver {
  330. logic.ReturnErrorResponse(
  331. w,
  332. r,
  333. logic.FormatError(errors.New("peer is acting as failover"), "badrequest"),
  334. )
  335. return
  336. }
  337. if node.IsFailOver {
  338. logic.ReturnErrorResponse(
  339. w,
  340. r,
  341. logic.FormatError(errors.New("node is acting as failover"), "badrequest"),
  342. )
  343. return
  344. }
  345. if peer.IsFailOver {
  346. logic.ReturnErrorResponse(
  347. w,
  348. r,
  349. logic.FormatError(errors.New("peer is acting as failover"), "badrequest"),
  350. )
  351. return
  352. }
  353. if node.IsRelayed && node.RelayedBy == peer.ID.String() {
  354. logic.ReturnErrorResponse(
  355. w,
  356. r,
  357. logic.FormatError(errors.New("node is relayed by peer node"), "badrequest"),
  358. )
  359. return
  360. }
  361. if node.IsRelay && peer.RelayedBy == node.ID.String() {
  362. logic.ReturnErrorResponse(
  363. w,
  364. r,
  365. logic.FormatError(errors.New("node acting as relay for the peer node"), "badrequest"),
  366. )
  367. return
  368. }
  369. if node.IsInternetGateway && peer.InternetGwID == node.ID.String() {
  370. logic.ReturnErrorResponse(
  371. w,
  372. r,
  373. logic.FormatError(
  374. errors.New("node acting as internet gw for the peer node"),
  375. "badrequest",
  376. ),
  377. )
  378. return
  379. }
  380. if node.InternetGwID != "" && node.InternetGwID == peer.ID.String() {
  381. logic.ReturnErrorResponse(
  382. w,
  383. r,
  384. logic.FormatError(
  385. errors.New("node using a internet gw by the peer node"),
  386. "badrequest",
  387. ),
  388. )
  389. return
  390. }
  391. // set failover ack
  392. err = proLogic.RegisterFailOverAck(nodeid, peerid)
  393. if err != nil {
  394. logic.ReturnErrorResponse(
  395. w,
  396. r,
  397. logic.FormatError(
  398. err,
  399. "internal",
  400. ),
  401. )
  402. }
  403. // check if both ack-exists
  404. ack1exists := proLogic.DoesFailoverAckExists(fmt.Sprintf("%s_%s", nodeid, peerid))
  405. ack2exists := proLogic.DoesFailoverAckExists(fmt.Sprintf("%s_%s", peerid, nodeid))
  406. if !ack1exists || !ack2exists {
  407. return
  408. }
  409. err = proLogic.SetFailOverCtx(failOverNode, node, peer)
  410. if err != nil {
  411. slog.Error("failed to create failover", "id", node.ID.String(),
  412. "network", node.Network, "error", err)
  413. logic.ReturnErrorResponse(
  414. w,
  415. r,
  416. logic.FormatError(fmt.Errorf("failed to create failover: %v", err), "internal"),
  417. )
  418. return
  419. }
  420. slog.Info(
  421. "[auto-relay] created relay on node",
  422. "node",
  423. node.ID.String(),
  424. "network",
  425. node.Network,
  426. )
  427. sendPeerUpdate = true
  428. if sendPeerUpdate {
  429. go func() {
  430. mq.PublishPeerUpdate(false)
  431. // delete ACK
  432. proLogic.DeRegisterFailOverAck(nodeid, peerid)
  433. }()
  434. }
  435. w.Header().Set("Content-Type", "application/json")
  436. logic.ReturnSuccessResponse(w, r, "relayed successfully")
  437. }