Browse Source

Home complete.

Frederic G. MARAND 1 year ago
parent
commit
f8e705c205
4 changed files with 125 additions and 66 deletions
  1. 2 2
      api/dlq.yml
  2. 2 2
      back/services/redriver/converters.go
  3. 84 31
      back/web/home.go
  4. 37 31
      front/templates/home.gohtml

+ 2 - 2
api/dlq.yml

@@ -226,7 +226,7 @@ components:
     QueueURL:
       type: string
       description: The absolute URL to the queue
-      example: https://sqs.amazonws.com/eu-west-3/12345678901234/some-queue
+      example: https://sqs.amazonaws.com/eu-west-3/12345678901234/some-queue
     QueueARN:
       type: string
       description: the Amazon resource name (ARN) of the queue.
@@ -308,7 +308,7 @@ components:
           approximate_number_of_messages_not_visible: 1
           approximate_number_of_messages: 0
           receive_message_wait_time_seconds: 3
-        url: https://sqs.amazonws.com/eu-west-3/12345678901234/some-queue
+        url: https://sqs.amazonaws.com/eu-west-3/12345678901234/some-queue
     name_redrive_body:
       type: object
       properties:

+ 2 - 2
back/services/redriver/converters.go

@@ -24,7 +24,7 @@ func ARNFromURL(qURL string) (arn.ARN, error) {
 		return a, fmt.Errorf("queue path %q does not have exactly 2 parts", path)
 	}
 	hostParts := strings.Split(u.Host, ".")
-	if len(hostParts) != 4 { // <service>.<region>.amazonws.com
+	if len(hostParts) != 4 { // <service>.<region>.amazonaws.com
 		return a, fmt.Errorf("queue host %q does not have exactly 4 parts", u.Host)
 	}
 
@@ -78,7 +78,7 @@ func URLFromARN(qARN arn.ARN) (name string, err error) {
 	}
 	u := url.URL{
 		Scheme: "https",
-		Host:   fmt.Sprintf("%s.%s.%s", qARN.Service, qARN.Region, "amazonws.com"),
+		Host:   fmt.Sprintf("%s.%s.%s", qARN.Service, qARN.Region, "amazonaws.com"),
 		Path:   path,
 	}
 	return u.String(), nil

+ 84 - 31
back/web/home.go

@@ -2,10 +2,12 @@ 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"
 
@@ -19,7 +21,7 @@ import (
 //   - sources: policies describing a queue with a DLQ
 //   - notSources: policies describing a queue without a DLQ
 //
-// Note that dead-letter queues may also by source queues, for multi-level processing, and vice-versa.
+// 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) {
@@ -84,7 +86,7 @@ type link struct{ URL, Text string }
 type queueCell struct {
 	Message string
 	Link    *link
-	Links   []link
+	Links   [][]link
 }
 
 type QueueRow [2]queueCell
@@ -105,43 +107,34 @@ func makeHomeHandler(rd redriver.Redriver) gin.HandlerFunc {
 			return
 		}
 		policies := policiesByURL(c, qURLs, rd, ctx)
-		byqueue, wild, closed, sources, notSources := classifyPolicies(policies)
+		byQueue, wild, closed, sources, notSources := classifyPolicies(policies)
 		// log.Printf("DLQs: %#v\nSRCs: %#v\nNoSRCs: %#v\n", maps.Keys(DLQs), maps.Keys(SRCs), NoSRCs)
-		l := len(byqueue)
-		if len(wild) > 0 {
-			l++
-		}
-		if len(closed) > 0 {
-			l++
-		}
-		if len(notSources) > 0 {
-			l++
-		}
-		rows := make([]QueueRow, 0, l) // Sources appears within a byQueue row
+		rows := make([]QueueRow, 0, len(byQueue)+3) // wild, closed, notSources: sources appear within a byQueue row
 
-		rows, err = prepareSingleLeft(wild, rows, "All source queues allowed")
+		rows, err = prepareSingleLeft(rows, wild, "All source queues allowed")
 		if err != nil {
-			log.Printf("failed converting queue URL %q: %v", qURLs, err)
+			log.Printf("failed preparing wild DLQs: %v", err)
 			c.HTML(http.StatusInternalServerError, "500", nil)
 			return
 		}
-		rows, err = prepareSingleLeft(closed, rows, "No source queue allowed")
+		rows, err = prepareSingleLeft(rows, closed, "No source queue allowed")
 		if err != nil {
-			log.Printf("failed converting queue URL %q: %v", qURLs, err)
+			log.Printf("failed preparing closed DLQs: %v", err)
 			c.HTML(http.StatusInternalServerError, "500", nil)
 			return
 		}
-		rows, err = prepareSingleRight(notSources, rows, "No associated DLQ")
+		rows, err = prepareSingleRight(rows, notSources, "No associated DLQ")
 		if err != nil {
-			log.Printf("failed converting queue URL %q: %v", qURLs, err)
+			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
 		}
-
-		// for qURLs := range closed {
-		//
-		// }
-		log.Println(sources)
 		c.HTML(http.StatusOK, "home", gin.H{
 			"flashes": flashes,
 			"latency": latency,
@@ -151,38 +144,98 @@ func makeHomeHandler(rd redriver.Redriver) gin.HandlerFunc {
 	}
 }
 
-func prepareSingleLeft(list map[string]redriver.QueueRedrivePolicies, rows []QueueRow, msg string) ([]QueueRow, error) {
+func prepareSingleLeft(rows []QueueRow, list map[string]redriver.QueueRedrivePolicies, msg string) ([]QueueRow, error) {
 	if len(list) > 0 {
 		row := QueueRow{
 			{Message: msg},
-			{Links: make([]link, 0, len(list))},
+			{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 = append(row[1].Links, link{URL: "/queue/" + name, Text: name})
+			row[1].Links[0] = append(row[1].Links[0], link{URL: "/queue/" + name, Text: name})
 		}
 		rows = append(rows, row)
 	}
 	return rows, nil
 }
 
-func prepareSingleRight(list []string, rows []QueueRow, msg string) ([]QueueRow, error) {
+func prepareSingleRight(rows []QueueRow, list []string, msg string) ([]QueueRow, error) {
 	if len(list) > 0 {
 		row := QueueRow{
-			{Links: make([]link, 0, len(list))},
+			{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 = append(row[0].Links, link{URL: "/queue/" + name, Text: name})
+			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
+}

+ 37 - 31
front/templates/home.gohtml

@@ -23,8 +23,8 @@
             {{ end}}
         </h1>
         <table id="home-table" class="table">
-            <caption>Grayed out queues in the left column are accepted by the given DLQ,
-                but are not configured to use a DLQ. This is a configuration error in SQS.
+            <caption>Having DLQs not allowing any source queue is a configuration anomaly.
+                So is having a DLQ allowing a source queue and that source using another DLQ instead.
             </caption>
             <thead>
             <th scope="col">Queue name</th>
@@ -34,29 +34,6 @@
             {{ range .rows }}
                 {{ template "home-row" . }}
             {{ end }}
-            <tr>
-                <td>
-                    <ul>
-                        <li><a class="text-black-50 bg-light" href="/queue/acquisition-reviews-sender-submit-qa3">acquisition-reviews-sender-submit-qa3</a>
-                        </li>
-                        <li>
-                            <a href="/queue/acquisition-reviews-sender-submit-stg">acquisition-reviews-sender-submit-stg</a>
-                        </li>
-                    </ul>
-                </td>
-                <td><a href="/queue/acquisition-reviews-sender-submit-dlq">acquisition-reviews-sender-submit-dlq</a>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <ul>
-                        <li><a href="/queue/payment-payin-work-dlq-qa3">payment-payin-work-dlq-qa3</a></li>
-                        <li><a class="text-black-50 bg-light" href="/queue/payment-payin-work-dlq-stg">payment-payin-work-dlq-std</a>
-                        </li>
-                    </ul>
-                </td>
-                <td><a href="/queue/payment-payin-work-dlq-dlq">payment-payin-work-dlq</a></td>
-            </tr>
             </tbody>
         </table>
     </main>
@@ -73,15 +50,44 @@
 
 {{ define "home-cell" }}
     {{ if .Message }}
-        <em>{{ .Message }}</em>
+        <p class="lead">{{ .Message }}</p>
     {{else if .Link }}
         <a href="{{ .Link.URL }}">{{ .Link.Text }}</a>
     {{ else }}
-
-        <ul>
+        {{/* Bound queues: 3 named lists */}}
+        {{ if eq (len .Links) 3 }}
+            {{ if index .Links 0 }}
+                <p class="lead">Bound queues</p>
+            {{end }}
+            <ul>
+                {{ range index .Links 0 }}
+                    <li><a href="{{ .URL }}">{{ .Text }}</a></li>
+                {{ end }}
+            </ul>
+            {{ if index .Links 1 }}
+                <p class="lead">Allowed source queues using another DLQ</p>
+            {{end }}
+            <ul>
+                {{ range index .Links 1 }}
+                    <li><a href="{{ .URL }}">{{ .Text }}</a></li>
+                {{ end }}
+            </ul>
+            {{ if index .Links 2 }}
+                <p class="lead">Allowed source queues not declaring a DLQ</p>
+            {{ end}}
+            <ul>
+                {{ range index .Links 2 }}
+                    <li><a href="{{ .URL }}">{{ .Text }}</a></li>
+                {{ end }}
+            </ul>
+        {{ else }}
             {{ range .Links }}
-                <li><a href="{{ .URL }}">{{ .Text }}</a></li>
+                <ul>
+                    {{ range . }}
+                        <li><a href="{{ .URL }}">{{ .Text }}</a></li>
+                    {{ end }}
+                </ul>
             {{ end }}
-        </ul>
-    {{end}}
+        {{end}}
+    {{ end }}
 {{ end }}