123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- package web
- import (
- "context"
- "fmt"
- "log"
- "net/http"
- "time"
- "github.com/aws/aws-sdk-go-v2/aws/arn"
- "github.com/gin-contrib/sessions"
- "github.com/gin-gonic/gin"
- "code.osinet.fr/fgm/sqs_demo/back/services/redriver"
- )
- // classifyPolicies distributes queue policies across five bins:
- // - wild: policies describing a DLQ with an allowAll policy
- // - closed: policies describing a DLQ with an denyAll policy
- // - byQueue: policies describing a DLQ with an byQueue policy
- // - sources: policies describing a queue with a DLQ
- // - notSources: policies describing a queue without a DLQ
- //
- // Note that dead-letter queues may also be source queues, for multi-level processing, and vice-versa.
- //
- // For easier reading, DLQs are not listed as part of the non-source list.
- func classifyPolicies(policies map[string]*redriver.QueueRedrivePolicies) (byQueue, wild, closed, sources map[string]redriver.QueueRedrivePolicies, notSources []string) {
- DLQs := make(map[string]redriver.QueueRedrivePolicies)
- sources = make(map[string]redriver.QueueRedrivePolicies)
- wild = make(map[string]redriver.QueueRedrivePolicies)
- closed = make(map[string]redriver.QueueRedrivePolicies)
- byQueue = make(map[string]redriver.QueueRedrivePolicies)
- nsTemp := make([]string, 0, len(notSources))
- for qURL, qrp := range policies {
- if qrp.QueueInfoAttributesRedriveAllowPolicy != nil {
- DLQs[qURL] = *qrp
- }
- if qrp.QueueInfoAttributesRedrivePolicy != nil {
- sources[qURL] = *qrp
- } else {
- nsTemp = append(nsTemp, qURL)
- }
- }
- for _, qURL := range nsTemp {
- if _, ok := DLQs[qURL]; !ok {
- notSources = append(notSources, qURL)
- }
- }
- for qURL, qrp := range DLQs {
- rap := qrp.QueueInfoAttributesRedriveAllowPolicy
- switch rap.RedrivePermission {
- case "allowAll":
- wild[qURL] = qrp
- case "denyAll":
- closed[qURL] = qrp
- case "byQueue":
- byQueue[qURL] = qrp
- }
- }
- return
- }
- func policiesByURL(c *gin.Context, qURLs []string, rd redriver.Redriver, ctx context.Context) map[string]*redriver.QueueRedrivePolicies {
- qMap := make(map[string]*redriver.QueueRedrivePolicies, len(qURLs))
- for _, qURL := range qURLs {
- name, err := redriver.NameFromURL(qURL)
- if err != nil {
- log.Println(http.StatusInternalServerError)
- c.HTML(http.StatusInternalServerError, "500", nil)
- }
- qrp, err := rd.GetRedrivePolicies(ctx, name)
- if err != nil || qrp == nil {
- log.Println(err)
- c.HTML(http.StatusInternalServerError, "500", nil)
- }
- qMap[qURL] = qrp
- }
- return qMap
- }
- type link struct{ URL, Text string }
- // queueRow provides templates with a representation for all kinds of table cells
- type queueCell struct {
- Message string
- Link *link
- Links [][]link
- }
- type QueueRow [2]queueCell
- func makeHomeHandler(rd redriver.Redriver, prefix string) gin.HandlerFunc {
- return func(c *gin.Context) {
- ctx := c.Request.Context()
- sess := sessions.Default(c)
- flashes := sess.Flashes()
- defer func() { _ = sess.Save() }()
- t0 := time.Now()
- qURLs, err := rd.ListQueues(ctx, prefix)
- latency := time.Since(t0)
- if err != nil {
- log.Printf("failed listing queues: %v", err)
- c.HTML(http.StatusInternalServerError, "500", nil)
- return
- }
- policies := policiesByURL(c, qURLs, rd, ctx)
- byQueue, wild, closed, sources, notSources := classifyPolicies(policies)
- // log.Printf("DLQs: %#v\nSRCs: %#v\nNoSRCs: %#v\n", maps.Keys(DLQs), maps.Keys(SRCs), NoSRCs)
- rows := make([]QueueRow, 0, len(byQueue)+3) // wild, closed, notSources: sources appear within a byQueue row
- rows, err = prepareSingleLeft(rows, wild, "All source queues allowed")
- if err != nil {
- log.Printf("failed preparing wild DLQs: %v", err)
- c.HTML(http.StatusInternalServerError, "500", nil)
- return
- }
- rows, err = prepareSingleLeft(rows, closed, "No source queue allowed")
- if err != nil {
- log.Printf("failed preparing closed DLQs: %v", err)
- c.HTML(http.StatusInternalServerError, "500", nil)
- return
- }
- rows, err = prepareSingleRight(rows, notSources, "No associated DLQ")
- if err != nil {
- log.Printf("failed converting isolated queues: %v", err)
- c.HTML(http.StatusInternalServerError, "500", nil)
- return
- }
- rows, err = prepareBindings(rows, byQueue, sources)
- if err != nil {
- log.Printf("failed preparing queue bindings: %v", err)
- c.HTML(http.StatusInternalServerError, "500", nil)
- return
- }
- c.HTML(http.StatusOK, "home", gin.H{
- "flashes": flashes,
- "latency": latency,
- "prefix": prefix,
- "rows": rows,
- })
- }
- }
- func prepareSingleLeft(rows []QueueRow, list map[string]redriver.QueueRedrivePolicies, msg string) ([]QueueRow, error) {
- if len(list) > 0 {
- row := QueueRow{
- {Message: msg},
- {Links: make([][]link, 1)},
- }
- row[1].Links[0] = make([]link, 0, len(list))
- for qURL := range list {
- name, err := redriver.NameFromURL(qURL)
- if err != nil {
- return nil, err
- }
- row[1].Links[0] = append(row[1].Links[0], link{URL: "/queue/" + name, Text: name})
- }
- rows = append(rows, row)
- }
- return rows, nil
- }
- func prepareSingleRight(rows []QueueRow, list []string, msg string) ([]QueueRow, error) {
- if len(list) > 0 {
- row := QueueRow{
- {Links: make([][]link, 1)},
- {Message: msg},
- }
- row[0].Links[0] = make([]link, 0, len(list))
- for _, qURL := range list {
- name, err := redriver.NameFromURL(qURL)
- if err != nil {
- return nil, err
- }
- row[0].Links[0] = append(row[0].Links[0], link{URL: "/queue/" + name, Text: name})
- }
- rows = append(rows, row)
- }
- return rows, nil
- }
- func prepareBinding(dlqURL string, DLQrp redriver.QueueRedrivePolicies, sources map[string]redriver.QueueRedrivePolicies) (*QueueRow, error) {
- var left, right queueCell
- dlqName, err := redriver.NameFromURL(dlqURL)
- if err != nil {
- return nil, fmt.Errorf("failed parsing DLQ URL %q: %w", dlqURL, err)
- }
- right = queueCell{Link: &link{URL: "/queue/" + dlqName, Text: dlqName}}
- a, err := redriver.ARNFromURL(dlqURL)
- if err != nil {
- return nil, fmt.Errorf("failed converting URL %q to ARN: %w", dlqURL, err)
- }
- dlqARN := a.String()
- var bound, unbound, elsewhere []link
- for _, sourceARN := range DLQrp.SourceQueueARNs {
- sa, err := arn.Parse(sourceARN)
- if err != nil {
- return nil, fmt.Errorf("failed parsing %q as an ARN: %w", sourceARN, err)
- }
- sourceURL, err := redriver.URLFromARN(sa)
- if err != nil {
- return nil, fmt.Errorf("failed converting ARN %q for URL: %w", sa.String(), err)
- }
- sourceName := sa.Resource
- sourceQRP, ok := sources[sourceURL]
- link := link{
- URL: "/queue/" + sourceName,
- Text: sourceName,
- }
- if ok {
- switch sourceQRP.DeadLetterTargetARN {
- case "":
- unbound = append(unbound, link)
- case dlqARN:
- bound = append(bound, link)
- default:
- elsewhere = append(elsewhere, link)
- }
- } else {
- unbound = append(unbound, link)
- }
- }
- left.Links = [][]link{bound, elsewhere, unbound}
- return &QueueRow{left, right}, nil
- }
- func prepareBindings(rows []QueueRow, DLQs, sources map[string]redriver.QueueRedrivePolicies) ([]QueueRow, error) {
- for dlqURL, DLQrp := range DLQs {
- row, err := prepareBinding(dlqURL, DLQrp, sources)
- if err != nil || row == nil {
- return nil, fmt.Errorf("failed binding DLQ %s: %w", dlqURL, err)
- }
- rows = append(rows, *row)
- }
- return rows, nil
- }
|