Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
# RABBITMQ_EXCHANGE="outpost"
# RABBITMQ_DELIVERY_QUEUE="outpost-delivery"
# RABBITMQ_LOG_QUEUE="outpost-log"
# RABBITMQ_DELIVERY_DLQ="outpost-delivery.dlq"
# RABBITMQ_LOG_DLQ="outpost-log.dlq"

## AWS SQS
# AWS_SQS_ENDPOINT="http://aws:4566"
Expand All @@ -50,6 +52,8 @@
# AWS_SQS_SECRET_ACCESS_KEY="test"
# AWS_SQS_DELIVERY_QUEUE="outpost-delivery"
# AWS_SQS_LOG_QUEUE="outpost-log"
# AWS_SQS_DELIVERY_DLQ="outpost-delivery-dlq"
# AWS_SQS_LOG_DLQ="outpost-log-dlq"

## GCP PubSub
# GCP_PUBSUB_PROJECT="test"
Expand All @@ -58,6 +62,10 @@
# GCP_PUBSUB_DELIVERY_SUBSCRIPTION="outpost-delivery-sub"
# GCP_PUBSUB_LOG_TOPIC="outpost-log"
# GCP_PUBSUB_LOG_SUBSCRIPTION="outpost-log-sub"
# GCP_PUBSUB_DELIVERY_DLQ_TOPIC="outpost-delivery-dlq"
# GCP_PUBSUB_DELIVERY_DLQ_SUBSCRIPTION="outpost-delivery-dlq-sub"
# GCP_PUBSUB_LOG_DLQ_TOPIC="outpost-log-dlq"
# GCP_PUBSUB_LOG_DLQ_SUBSCRIPTION="outpost-log-dlq-sub"

## Azure ServiceBus
# AZURE_SERVICEBUS_TENANT_ID=""
Expand Down
8 changes: 4 additions & 4 deletions docs/content/self-hosting/guides/byo-mqs.mdoc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The following table shows the resources, default names, and configuration variab
|----------|--------------------|-----------------|-----------------------|
| Exchange | `outpost` | `outpost` | `RABBITMQ_EXCHANGE` |
| Queue | `outpost-delivery` | `outpost-log` | `RABBITMQ_DELIVERY_QUEUE` / `RABBITMQ_LOG_QUEUE` |
| DLQ | `outpost-delivery.dlq` | `outpost-log.dlq` | (auto-derived from queue name) |
| DLQ | `outpost-delivery.dlq` | `outpost-log.dlq` | `RABBITMQ_DELIVERY_DLQ` / `RABBITMQ_LOG_DLQ` (optional; defaults to `<queue>.dlq` if unset) |

**Configuration requirements:**
- Exchange type: `topic`
Expand All @@ -84,7 +84,7 @@ The following table shows the resources, default names, and configuration variab
| Resource | Delivery MQ Default | Log MQ Default | Environment Variable |
|----------|--------------------|-----------------|-----------------------|
| Queue | `outpost-delivery` | `outpost-log` | `AWS_SQS_DELIVERY_QUEUE` / `AWS_SQS_LOG_QUEUE` |
| DLQ | `outpost-delivery-dlq` | `outpost-log-dlq` | (auto-derived from queue name) |
| DLQ | `outpost-delivery-dlq` | `outpost-log-dlq` | `AWS_SQS_DELIVERY_DLQ` / `AWS_SQS_LOG_DLQ` (optional; defaults to `<queue>-dlq` if unset) |

**Configuration requirements:**
- Configure `RedrivePolicy` on main queue pointing to DLQ
Expand All @@ -98,8 +98,8 @@ The following table shows the resources, default names, and configuration variab
|----------|--------------------|-----------------|-----------------------|
| Topic | `outpost-delivery` | `outpost-log` | `GCP_PUBSUB_DELIVERY_TOPIC` / `GCP_PUBSUB_LOG_TOPIC` |
| Subscription | `outpost-delivery-sub` | `outpost-log-sub` | `GCP_PUBSUB_DELIVERY_SUBSCRIPTION` / `GCP_PUBSUB_LOG_SUBSCRIPTION` |
| DLQ Topic | `outpost-delivery-dlq` | `outpost-log-dlq` | (auto-derived from topic name) |
| DLQ Subscription | `outpost-delivery-dlq-sub` | `outpost-log-dlq-sub` | (auto-derived from topic name) |
| DLQ Topic | `outpost-delivery-dlq` | `outpost-log-dlq` | `GCP_PUBSUB_DELIVERY_DLQ_TOPIC` / `GCP_PUBSUB_LOG_DLQ_TOPIC` (optional; defaults to `<topic>-dlq` if unset) |
| DLQ Subscription | `outpost-delivery-dlq-sub` | `outpost-log-dlq-sub` | `GCP_PUBSUB_DELIVERY_DLQ_SUBSCRIPTION` / `GCP_PUBSUB_LOG_DLQ_SUBSCRIPTION` (optional; defaults to `<dlq-topic>-sub` if unset) |

**Configuration requirements:**
- Create both topic and subscription pairs
Expand Down
8 changes: 8 additions & 0 deletions internal/config/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ func (c *Config) getMQSpecificFields(mqType string) []zap.Field {
zap.String("rabbitmq_exchange", c.MQs.RabbitMQ.Exchange),
zap.String("rabbitmq_delivery_queue", c.MQs.RabbitMQ.DeliveryQueue),
zap.String("rabbitmq_log_queue", c.MQs.RabbitMQ.LogQueue),
zap.String("rabbitmq_delivery_dlq", c.MQs.RabbitMQ.getDLQName("deliverymq")),
zap.String("rabbitmq_log_dlq", c.MQs.RabbitMQ.getDLQName("logmq")),
}
case "awssqs":
return []zap.Field{
Expand All @@ -170,6 +172,8 @@ func (c *Config) getMQSpecificFields(mqType string) []zap.Field {
zap.String("aws_region", c.MQs.AWSSQS.Region),
zap.String("aws_delivery_queue", c.MQs.AWSSQS.DeliveryQueue),
zap.String("aws_log_queue", c.MQs.AWSSQS.LogQueue),
zap.String("aws_delivery_dlq", c.MQs.AWSSQS.getDLQName("deliverymq")),
zap.String("aws_log_dlq", c.MQs.AWSSQS.getDLQName("logmq")),
}
case "gcppubsub":
return []zap.Field{
Expand All @@ -179,6 +183,10 @@ func (c *Config) getMQSpecificFields(mqType string) []zap.Field {
zap.String("gcp_delivery_subscription", c.MQs.GCPPubSub.DeliverySubscription),
zap.String("gcp_log_topic", c.MQs.GCPPubSub.LogTopic),
zap.String("gcp_log_subscription", c.MQs.GCPPubSub.LogSubscription),
zap.String("gcp_delivery_dlq_topic", c.MQs.GCPPubSub.getDLQTopicByQueueType("deliverymq")),
zap.String("gcp_delivery_dlq_subscription", c.MQs.GCPPubSub.getDLQSubscriptionByQueueType("deliverymq")),
zap.String("gcp_log_dlq_topic", c.MQs.GCPPubSub.getDLQTopicByQueueType("logmq")),
zap.String("gcp_log_dlq_subscription", c.MQs.GCPPubSub.getDLQSubscriptionByQueueType("logmq")),
}
case "azureservicebus":
return []zap.Field{
Expand Down
22 changes: 22 additions & 0 deletions internal/config/mqconfig_aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type AWSSQSConfig struct {
Endpoint string `yaml:"endpoint" env:"AWS_SQS_ENDPOINT" desc:"Custom AWS SQS endpoint URL. Optional, typically used for local testing (e.g., LocalStack)." required:"N"`
DeliveryQueue string `yaml:"delivery_queue" env:"AWS_SQS_DELIVERY_QUEUE" desc:"Name of the SQS queue for delivery events." required:"N"`
LogQueue string `yaml:"log_queue" env:"AWS_SQS_LOG_QUEUE" desc:"Name of the SQS queue for log events." required:"N"`
DeliveryDLQ string `yaml:"delivery_dlq" env:"AWS_SQS_DELIVERY_DLQ" desc:"Name of the dead-letter queue for the delivery queue. Optional; defaults to '<delivery_queue>-dlq' if unset." required:"N"`
LogDLQ string `yaml:"log_dlq" env:"AWS_SQS_LOG_DLQ" desc:"Name of the dead-letter queue for the log queue. Optional; defaults to '<log_queue>-dlq' if unset." required:"N"`
}

func (c *AWSSQSConfig) getQueueName(queueType string) string {
Expand All @@ -33,6 +35,25 @@ func (c *AWSSQSConfig) getQueueName(queueType string) string {
}
}

func (c *AWSSQSConfig) getDLQName(queueType string) string {
var dlq string
switch queueType {
case "deliverymq":
dlq = c.DeliveryDLQ
case "logmq":
dlq = c.LogDLQ
default:
return ""
}
if dlq != "" {
return dlq
}
if queue := c.getQueueName(queueType); queue != "" {
return mqinfra.DefaultAWSSQSDLQName(queue)
}
return ""
}

func (c *AWSSQSConfig) getCredentials() string {
return fmt.Sprintf("%s:%s:", c.AccessKeyID, c.SecretAccessKey)
}
Expand All @@ -44,6 +65,7 @@ func (c *AWSSQSConfig) ToInfraConfig(queueType string) *mqinfra.MQInfraConfig {
Region: c.Region,
ServiceAccountCredentials: c.getCredentials(),
Topic: c.getQueueName(queueType),
DLQ: c.getDLQName(queueType),
},
}
}
Expand Down
63 changes: 63 additions & 0 deletions internal/config/mqconfig_aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,69 @@ func TestAWSSQSConfig_IsConfigured(t *testing.T) {
}
}

func TestAWSSQSConfig_DLQName(t *testing.T) {
t.Parallel()

tests := []struct {
name string
cfg config.AWSSQSConfig
queueType string
want string
}{
{
name: "delivery falls back to derived name",
cfg: config.AWSSQSConfig{Region: "us-east-1", DeliveryQueue: "outpost-delivery"},
queueType: "deliverymq",
want: "outpost-delivery-dlq",
},
{
name: "log falls back to derived name",
cfg: config.AWSSQSConfig{Region: "us-east-1", LogQueue: "outpost-log"},
queueType: "logmq",
want: "outpost-log-dlq",
},
{
name: "delivery override wins",
cfg: config.AWSSQSConfig{Region: "us-east-1", DeliveryQueue: "outpost-delivery", DeliveryDLQ: "dead_letter-outpost-delivery"},
queueType: "deliverymq",
want: "dead_letter-outpost-delivery",
},
{
name: "log override wins",
cfg: config.AWSSQSConfig{Region: "us-east-1", LogQueue: "outpost-log", LogDLQ: "dead_letter-outpost-log"},
queueType: "logmq",
want: "dead_letter-outpost-log",
},
{
name: "log override does not leak into delivery",
cfg: config.AWSSQSConfig{Region: "us-east-1", DeliveryQueue: "outpost-delivery", LogDLQ: "dead_letter-outpost-log"},
queueType: "deliverymq",
want: "outpost-delivery-dlq",
},
{
name: "no queue name yields no dlq name",
cfg: config.AWSSQSConfig{Region: "us-east-1"},
queueType: "deliverymq",
want: "",
},
{
name: "unknown queue type yields no dlq name",
cfg: config.AWSSQSConfig{Region: "us-east-1", DeliveryQueue: "outpost-delivery"},
queueType: "somethingelse",
want: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
infraCfg := tt.cfg.ToInfraConfig(tt.queueType)
require.NotNil(t, infraCfg.AWSSQS)
assert.Equal(t, tt.want, infraCfg.AWSSQS.DLQ)
})
}
}

func TestAWSSQSConfig_ToQueueConfig(t *testing.T) {
t.Parallel()

Expand Down
3 changes: 3 additions & 0 deletions internal/config/mqconfig_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type AzureServiceBusConfig struct {
LogTopic string `yaml:"log_topic" env:"AZURE_SERVICEBUS_LOG_TOPIC" desc:"Topic name for log queue" required:"N" default:"outpost-log"`
LogSubscription string `yaml:"log_subscription" env:"AZURE_SERVICEBUS_LOG_SUBSCRIPTION" desc:"Subscription name for log queue" required:"N" default:"outpost-log-sub"`

// No DLQ name settings: Azure dead-letters into each subscription's built-in
// $DeadLetterQueue sub-queue, whose name is fixed by the platform.

// connectionStringOnce sync.Once
// connectionString string
// connectionStringError error
Expand Down
44 changes: 44 additions & 0 deletions internal/config/mqconfig_gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ type GCPPubSubConfig struct {
DeliverySubscription string `yaml:"delivery_subscription" env:"GCP_PUBSUB_DELIVERY_SUBSCRIPTION" desc:"Name of the GCP Pub/Sub subscription for delivery events." required:"N"`
LogTopic string `yaml:"log_topic" env:"GCP_PUBSUB_LOG_TOPIC" desc:"Name of the GCP Pub/Sub topic for log events." required:"N"`
LogSubscription string `yaml:"log_subscription" env:"GCP_PUBSUB_LOG_SUBSCRIPTION" desc:"Name of the GCP Pub/Sub subscription for log events." required:"N"`
DeliveryDLQTopic string `yaml:"delivery_dlq_topic" env:"GCP_PUBSUB_DELIVERY_DLQ_TOPIC" desc:"Name of the GCP Pub/Sub dead-letter topic for delivery events. Optional; defaults to '<delivery_topic>-dlq' if unset." required:"N"`
DeliveryDLQSubscription string `yaml:"delivery_dlq_subscription" env:"GCP_PUBSUB_DELIVERY_DLQ_SUBSCRIPTION" desc:"Name of the GCP Pub/Sub subscription on the delivery dead-letter topic. Optional; defaults to '<delivery_dlq_topic>-sub' if unset." required:"N"`
LogDLQTopic string `yaml:"log_dlq_topic" env:"GCP_PUBSUB_LOG_DLQ_TOPIC" desc:"Name of the GCP Pub/Sub dead-letter topic for log events. Optional; defaults to '<log_topic>-dlq' if unset." required:"N"`
LogDLQSubscription string `yaml:"log_dlq_subscription" env:"GCP_PUBSUB_LOG_DLQ_SUBSCRIPTION" desc:"Name of the GCP Pub/Sub subscription on the log dead-letter topic. Optional; defaults to '<log_dlq_topic>-sub' if unset." required:"N"`
}

func (c *GCPPubSubConfig) getTopicByQueueType(queueType string) string {
Expand All @@ -39,13 +43,53 @@ func (c *GCPPubSubConfig) getSubscriptionByQueueType(queueType string) string {
}
}

func (c *GCPPubSubConfig) getDLQTopicByQueueType(queueType string) string {
var dlqTopic string
switch queueType {
case "deliverymq":
dlqTopic = c.DeliveryDLQTopic
case "logmq":
dlqTopic = c.LogDLQTopic
default:
return ""
}
if dlqTopic != "" {
return dlqTopic
}
if topic := c.getTopicByQueueType(queueType); topic != "" {
return mqinfra.DefaultGCPPubSubDLQTopicName(topic)
}
return ""
}

func (c *GCPPubSubConfig) getDLQSubscriptionByQueueType(queueType string) string {
var dlqSub string
switch queueType {
case "deliverymq":
dlqSub = c.DeliveryDLQSubscription
case "logmq":
dlqSub = c.LogDLQSubscription
default:
return ""
}
if dlqSub != "" {
return dlqSub
}
if dlqTopic := c.getDLQTopicByQueueType(queueType); dlqTopic != "" {
return mqinfra.DefaultGCPPubSubDLQSubscriptionName(dlqTopic)
}
return ""
}

func (c *GCPPubSubConfig) ToInfraConfig(queueType string) *mqinfra.MQInfraConfig {
return &mqinfra.MQInfraConfig{
GCPPubSub: &mqinfra.GCPPubSubInfraConfig{
ProjectID: c.Project,
ServiceAccountCredentials: c.ServiceAccountCredentials,
TopicID: c.getTopicByQueueType(queueType),
SubscriptionID: c.getSubscriptionByQueueType(queueType),
DLQTopicID: c.getDLQTopicByQueueType(queueType),
DLQSubscriptionID: c.getDLQSubscriptionByQueueType(queueType),
},
}
}
Expand Down
88 changes: 88 additions & 0 deletions internal/config/mqconfig_gcp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package config_test

import (
"testing"

"github.com/hookdeck/outpost/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGCPPubSubConfig_DLQNames(t *testing.T) {
t.Parallel()

tests := []struct {
name string
cfg config.GCPPubSubConfig
queueType string
wantTopic string
wantSub string
}{
{
name: "delivery falls back to derived names",
cfg: config.GCPPubSubConfig{Project: "p", DeliveryTopic: "outpost-delivery"},
queueType: "deliverymq",
wantTopic: "outpost-delivery-dlq",
wantSub: "outpost-delivery-dlq-sub",
},
{
name: "log falls back to derived names",
cfg: config.GCPPubSubConfig{Project: "p", LogTopic: "outpost-log"},
queueType: "logmq",
wantTopic: "outpost-log-dlq",
wantSub: "outpost-log-dlq-sub",
},
{
name: "topic and subscription overrides both win",
cfg: config.GCPPubSubConfig{Project: "p", DeliveryTopic: "outpost-delivery", DeliveryDLQTopic: "dead_letter-delivery", DeliveryDLQSubscription: "dead_letter-delivery-consumer"},
queueType: "deliverymq",
wantTopic: "dead_letter-delivery",
wantSub: "dead_letter-delivery-consumer",
},
{
name: "derived subscription follows an overridden dlq topic",
cfg: config.GCPPubSubConfig{Project: "p", DeliveryTopic: "outpost-delivery", DeliveryDLQTopic: "dead_letter-delivery"},
queueType: "deliverymq",
wantTopic: "dead_letter-delivery",
wantSub: "dead_letter-delivery-sub",
},
{
name: "subscription override alone leaves topic derived",
cfg: config.GCPPubSubConfig{Project: "p", DeliveryTopic: "outpost-delivery", DeliveryDLQSubscription: "dead_letter-delivery-consumer"},
queueType: "deliverymq",
wantTopic: "outpost-delivery-dlq",
wantSub: "dead_letter-delivery-consumer",
},
{
name: "log overrides do not leak into delivery",
cfg: config.GCPPubSubConfig{Project: "p", DeliveryTopic: "outpost-delivery", LogDLQTopic: "dead_letter-log", LogDLQSubscription: "dead_letter-log-consumer"},
queueType: "deliverymq",
wantTopic: "outpost-delivery-dlq",
wantSub: "outpost-delivery-dlq-sub",
},
{
name: "no topic name yields no dlq names",
cfg: config.GCPPubSubConfig{Project: "p"},
queueType: "deliverymq",
wantTopic: "",
wantSub: "",
},
{
name: "unknown queue type yields no dlq names",
cfg: config.GCPPubSubConfig{Project: "p", DeliveryTopic: "outpost-delivery"},
queueType: "somethingelse",
wantTopic: "",
wantSub: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
infraCfg := tt.cfg.ToInfraConfig(tt.queueType)
require.NotNil(t, infraCfg.GCPPubSub)
assert.Equal(t, tt.wantTopic, infraCfg.GCPPubSub.DLQTopicID)
assert.Equal(t, tt.wantSub, infraCfg.GCPPubSub.DLQSubscriptionID)
})
}
}
Loading
Loading