home.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package web
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "time"
  8. "github.com/aws/aws-sdk-go-v2/aws/arn"
  9. "github.com/gin-contrib/sessions"
  10. "github.com/gin-gonic/gin"
  11. "code.osinet.fr/fgm/sqs_demo/back/services/redriver"
  12. )
  13. // classifyPolicies distributes queue policies across five bins:
  14. // - wild: policies describing a DLQ with an allowAll policy
  15. // - closed: policies describing a DLQ with an denyAll policy
  16. // - byQueue: policies describing a DLQ with an byQueue policy
  17. // - sources: policies describing a queue with a DLQ
  18. // - notSources: policies describing a queue without a DLQ
  19. //
  20. // Note that dead-letter queues may also be source queues, for multi-level processing, and vice-versa.
  21. //
  22. // For easier reading, DLQs are not listed as part of the non-source list.
  23. func classifyPolicies(policies map[string]*redriver.QueueRedrivePolicies) (byQueue, wild, closed, sources map[string]redriver.QueueRedrivePolicies, notSources []string) {
  24. DLQs := make(map[string]redriver.QueueRedrivePolicies)
  25. sources = make(map[string]redriver.QueueRedrivePolicies)
  26. wild = make(map[string]redriver.QueueRedrivePolicies)
  27. closed = make(map[string]redriver.QueueRedrivePolicies)
  28. byQueue = make(map[string]redriver.QueueRedrivePolicies)
  29. nsTemp := make([]string, 0, len(notSources))
  30. for qURL, qrp := range policies {
  31. if qrp.QueueInfoAttributesRedriveAllowPolicy != nil {
  32. DLQs[qURL] = *qrp
  33. }
  34. if qrp.QueueInfoAttributesRedrivePolicy != nil {
  35. sources[qURL] = *qrp
  36. } else {
  37. nsTemp = append(nsTemp, qURL)
  38. }
  39. }
  40. for _, qURL := range nsTemp {
  41. if _, ok := DLQs[qURL]; !ok {
  42. notSources = append(notSources, qURL)
  43. }
  44. }
  45. for qURL, qrp := range DLQs {
  46. rap := qrp.QueueInfoAttributesRedriveAllowPolicy
  47. switch rap.RedrivePermission {
  48. case "allowAll":
  49. wild[qURL] = qrp
  50. case "denyAll":
  51. closed[qURL] = qrp
  52. case "byQueue":
  53. byQueue[qURL] = qrp
  54. }
  55. }
  56. return
  57. }
  58. func policiesByURL(c *gin.Context, qURLs []string, rd redriver.Redriver, ctx context.Context) map[string]*redriver.QueueRedrivePolicies {
  59. qMap := make(map[string]*redriver.QueueRedrivePolicies, len(qURLs))
  60. for _, qURL := range qURLs {
  61. name, err := redriver.NameFromURL(qURL)
  62. if err != nil {
  63. log.Println(http.StatusInternalServerError)
  64. c.HTML(http.StatusInternalServerError, "500", nil)
  65. }
  66. qrp, err := rd.GetRedrivePolicies(ctx, name)
  67. if err != nil || qrp == nil {
  68. log.Println(err)
  69. c.HTML(http.StatusInternalServerError, "500", nil)
  70. }
  71. qMap[qURL] = qrp
  72. }
  73. return qMap
  74. }
  75. type link struct{ URL, Text string }
  76. // queueRow provides templates with a representation for all kinds of table cells
  77. type queueCell struct {
  78. Message string
  79. Link *link
  80. Links [][]link
  81. }
  82. type QueueRow [2]queueCell
  83. func makeHomeHandler(rd redriver.Redriver, prefix string) gin.HandlerFunc {
  84. return func(c *gin.Context) {
  85. ctx := c.Request.Context()
  86. sess := sessions.Default(c)
  87. flashes := sess.Flashes()
  88. defer func() { _ = sess.Save() }()
  89. t0 := time.Now()
  90. qURLs, err := rd.ListQueues(ctx, prefix)
  91. latency := time.Since(t0)
  92. if err != nil {
  93. log.Printf("failed listing queues: %v", err)
  94. c.HTML(http.StatusInternalServerError, "500", nil)
  95. return
  96. }
  97. policies := policiesByURL(c, qURLs, rd, ctx)
  98. byQueue, wild, closed, sources, notSources := classifyPolicies(policies)
  99. // log.Printf("DLQs: %#v\nSRCs: %#v\nNoSRCs: %#v\n", maps.Keys(DLQs), maps.Keys(SRCs), NoSRCs)
  100. rows := make([]QueueRow, 0, len(byQueue)+3) // wild, closed, notSources: sources appear within a byQueue row
  101. rows, err = prepareSingleLeft(rows, wild, "All source queues allowed")
  102. if err != nil {
  103. log.Printf("failed preparing wild DLQs: %v", err)
  104. c.HTML(http.StatusInternalServerError, "500", nil)
  105. return
  106. }
  107. rows, err = prepareSingleLeft(rows, closed, "No source queue allowed")
  108. if err != nil {
  109. log.Printf("failed preparing closed DLQs: %v", err)
  110. c.HTML(http.StatusInternalServerError, "500", nil)
  111. return
  112. }
  113. rows, err = prepareSingleRight(rows, notSources, "No associated DLQ")
  114. if err != nil {
  115. log.Printf("failed converting isolated queues: %v", err)
  116. c.HTML(http.StatusInternalServerError, "500", nil)
  117. return
  118. }
  119. rows, err = prepareBindings(rows, byQueue, sources)
  120. if err != nil {
  121. log.Printf("failed preparing queue bindings: %v", err)
  122. c.HTML(http.StatusInternalServerError, "500", nil)
  123. return
  124. }
  125. c.HTML(http.StatusOK, "home", gin.H{
  126. "flashes": flashes,
  127. "latency": latency,
  128. "prefix": prefix,
  129. "rows": rows,
  130. })
  131. }
  132. }
  133. func prepareSingleLeft(rows []QueueRow, list map[string]redriver.QueueRedrivePolicies, msg string) ([]QueueRow, error) {
  134. if len(list) > 0 {
  135. row := QueueRow{
  136. {Message: msg},
  137. {Links: make([][]link, 1)},
  138. }
  139. row[1].Links[0] = make([]link, 0, len(list))
  140. for qURL := range list {
  141. name, err := redriver.NameFromURL(qURL)
  142. if err != nil {
  143. return nil, err
  144. }
  145. row[1].Links[0] = append(row[1].Links[0], link{URL: "/queue/" + name, Text: name})
  146. }
  147. rows = append(rows, row)
  148. }
  149. return rows, nil
  150. }
  151. func prepareSingleRight(rows []QueueRow, list []string, msg string) ([]QueueRow, error) {
  152. if len(list) > 0 {
  153. row := QueueRow{
  154. {Links: make([][]link, 1)},
  155. {Message: msg},
  156. }
  157. row[0].Links[0] = make([]link, 0, len(list))
  158. for _, qURL := range list {
  159. name, err := redriver.NameFromURL(qURL)
  160. if err != nil {
  161. return nil, err
  162. }
  163. row[0].Links[0] = append(row[0].Links[0], link{URL: "/queue/" + name, Text: name})
  164. }
  165. rows = append(rows, row)
  166. }
  167. return rows, nil
  168. }
  169. func prepareBinding(dlqURL string, DLQrp redriver.QueueRedrivePolicies, sources map[string]redriver.QueueRedrivePolicies) (*QueueRow, error) {
  170. var left, right queueCell
  171. dlqName, err := redriver.NameFromURL(dlqURL)
  172. if err != nil {
  173. return nil, fmt.Errorf("failed parsing DLQ URL %q: %w", dlqURL, err)
  174. }
  175. right = queueCell{Link: &link{URL: "/queue/" + dlqName, Text: dlqName}}
  176. a, err := redriver.ARNFromURL(dlqURL)
  177. if err != nil {
  178. return nil, fmt.Errorf("failed converting URL %q to ARN: %w", dlqURL, err)
  179. }
  180. dlqARN := a.String()
  181. var bound, unbound, elsewhere []link
  182. for _, sourceARN := range DLQrp.SourceQueueARNs {
  183. sa, err := arn.Parse(sourceARN)
  184. if err != nil {
  185. return nil, fmt.Errorf("failed parsing %q as an ARN: %w", sourceARN, err)
  186. }
  187. sourceURL, err := redriver.URLFromARN(sa)
  188. if err != nil {
  189. return nil, fmt.Errorf("failed converting ARN %q for URL: %w", sa.String(), err)
  190. }
  191. sourceName := sa.Resource
  192. sourceQRP, ok := sources[sourceURL]
  193. link := link{
  194. URL: "/queue/" + sourceName,
  195. Text: sourceName,
  196. }
  197. if ok {
  198. switch sourceQRP.DeadLetterTargetARN {
  199. case "":
  200. unbound = append(unbound, link)
  201. case dlqARN:
  202. bound = append(bound, link)
  203. default:
  204. elsewhere = append(elsewhere, link)
  205. }
  206. } else {
  207. unbound = append(unbound, link)
  208. }
  209. }
  210. left.Links = [][]link{bound, elsewhere, unbound}
  211. return &QueueRow{left, right}, nil
  212. }
  213. func prepareBindings(rows []QueueRow, DLQs, sources map[string]redriver.QueueRedrivePolicies) ([]QueueRow, error) {
  214. for dlqURL, DLQrp := range DLQs {
  215. row, err := prepareBinding(dlqURL, DLQrp, sources)
  216. if err != nil || row == nil {
  217. return nil, fmt.Errorf("failed binding DLQ %s: %w", dlqURL, err)
  218. }
  219. rows = append(rows, *row)
  220. }
  221. return rows, nil
  222. }