feat(aws-compat): queue attributes for Terraform (FifoQueue, ContentBasedDeduplication, Policy, real timestamps)

- GetQueueAttributes now returns FifoQueue, ContentBasedDeduplication, Policy and
  real Created/LastModifiedTimestamp (previously missing/stubbed '0000000000')
- Queue: add CreatedAt, LastModifiedAt, Policy, ContentBasedDeduplication
- parse FifoQueue/ContentBasedDeduplication in both protocols (StringToBool)
- Policy/RedriveAllowPolicy: normalize JSON-string form to object (JSON protocol
  sent them as string -> SetQueueAttributes failed with InvalidParameterValue)
- persistence: keep new fields in meta DTO + backfill empty timestamps on load
- FIFO dedup is inherent: IsDuplicate/InitDuplicatation gate on IsFIFO only
  (env flag EnableDuplicates=false disabled dedup for all FIFO queues)
This commit is contained in:
Repinoid
2026-10-10 17:29:58 +03:00
parent 22fa2b162a
commit 9f1242aca3
7 changed files with 171 additions and 16 deletions
+23 -2
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"strconv"
"strings"
"time"
"shared-sqs/app/interfaces"
"shared-sqs/app/models"
@@ -86,6 +87,13 @@ func GetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBo
if shouldInclude("VisibilityTimeout") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "VisibilityTimeout", Value: strconv.Itoa(queue.VisibilityTimeout)})
}
// FIFO-атрибуты: читаются Terraform (aws_sqs_queue) и aws cli; без них — drift.
if shouldInclude("FifoQueue") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "FifoQueue", Value: strconv.FormatBool(queue.IsFIFO)})
}
if shouldInclude("ContentBasedDeduplication") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "ContentBasedDeduplication", Value: strconv.FormatBool(queue.ContentBasedDeduplication)})
}
if shouldInclude("ApproximateNumberOfMessages") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "ApproximateNumberOfMessages", Value: strconv.Itoa(queue.LiveCount())})
}
@@ -93,10 +101,10 @@ func GetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBo
queueAttributes = append(queueAttributes, models.Attribute{Name: "ApproximateNumberOfMessagesNotVisible", Value: strconv.Itoa(numberOfHiddenMessagesInQueue(queue))})
}
if shouldInclude("CreatedTimestamp") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "CreatedTimestamp", Value: "0000000000"})
queueAttributes = append(queueAttributes, models.Attribute{Name: "CreatedTimestamp", Value: epochSeconds(queue.CreatedAt)})
}
if shouldInclude("LastModifiedTimestamp") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "LastModifiedTimestamp", Value: "0000000000"})
queueAttributes = append(queueAttributes, models.Attribute{Name: "LastModifiedTimestamp", Value: epochSeconds(queue.LastModifiedAt)})
}
if shouldInclude("QueueArn") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "QueueArn", Value: queue.Arn})
@@ -107,6 +115,10 @@ func GetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBo
Value: fmt.Sprintf(`{"maxReceiveCount":"%d", "deadLetterTargetArn":"%s"}`, queue.MaxReceiveCount, queue.DeadLetterQueue.Arn),
})
}
// Policy — отдаём сохранённую JSON-строку как есть (совместимость с Terraform).
if shouldInclude("Policy") && queue.Policy != "" {
queueAttributes = append(queueAttributes, models.Attribute{Name: "Policy", Value: queue.Policy})
}
respStruct := models.GetQueueAttributesResponse{
Xmlns: models.BaseXmlns,
@@ -115,3 +127,12 @@ func GetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBo
}
return http.StatusOK, respStruct
}
// epochSeconds — timestamp в формате AWS (unix-секунды строкой).
// Нулевое время отдаём как "0" (стабильно, без drift при чтении).
func epochSeconds(t time.Time) string {
if t.IsZero() {
return "0"
}
return strconv.FormatInt(t.Unix(), 10)
}