HopAlongPolly
HopAlongPolly

Reputation: 1443

Localstack SNS Topic IS NOT Forwarding Message Body to Subscribed HTTP Endpoint

The Problem

An SNS Topic running in a LocalStack Container will send a notification to a subscribed HTTP endpoint running in a separate container, but the API receives {} instead of the message sent to SNS.

Steps To Reproduce

For brevity I excluded the my-api Dockerfile and App code. You can fill in any web server (FastAPI, Express etc.) just so long as it listens on Port 3000, and has a POST /events endpoint. There's nothing special going on in that API right now on my end, it simply logs the event received.

  1. Create the following docker-compose.yml file
version: '3.8'

services:
  my-api:
    build:
      context: .
    ports:
      - 3000:3000
    volumes:
      - ./src:/opt/my-api/src

  localstack:
    image: localstack/localstack
    container_name: localstack
    ports:
      - 4566:4566
    environment:
      - SERVICES=sns
      - AWS_DEFAULT_REGION=us-east-1
    volumes:
      - localstack-data:/var/lib/localstack
      # https://docs.localstack.cloud/references/init-hooks/
      - ./localstack-init-scripts/:/etc/localstack/init/ready.d/

volumes:
  localstack-data:

  1. Create the localstack-init-scripts/init.py Python script for initializing the LocalStack container.
import boto3
import requests

# SNS Client
sns = boto3.client('sns', endpoint_url='http://localhost:4566', region_name='us-east-1')

# Create Topic
topic_arn = sns.create_topic(Name='local-events')['TopicArn']
print('Topic Arn:', topic_arn)

#Subscribe HTTP endpoint to Topic
subscription_arn = sns.subscribe(
    TopicArn=topic_arn,
    Protocol='http',
    Endpoint='http://host.docker.internal:3000/events',
    # I've also tried Endpoint='http://my-api:3000/events',
    ReturnSubscriptionArn=True
)['SubscriptionArn']
print('Subscription Arn:', subscription_arn)

# Get the Subscription Token and Confirm the Subscription
token_res = requests.get(f'http://localhost:4566/_aws/sns/subscription-tokens/{subscription_arn}').json()
print('Token Res:', token_res)
confirmation_status_code = sns.confirm_subscription(
    TopicArn=topic_arn,
    Token=token_res['subscription_token']
).get('ResponseMetadata', {}).get('HTTPStatusCode', 500)

confirmed = True if confirmation_status_code == 200 else False 
print('Subscription Confirmed:', confirmed)

  1. Spin up Docker services
docker compose up
  1. Send a test notification to the local-events Topic
aws --endpoint-url http://localhost:4566 sns publish \
  --topic-arn arn:aws:sns:us-east-1:000000000000:local-events \
  --message '{ "msg": "This is a Test Message" }'

Results

After running the above commands you should see docker logs similar to

localstack                    | 2024-10-01T01:38:29.993  INFO --- [et.reactor-0] localstack.request.aws     : AWS sns.CreateTopic => 200
localstack                    | Topic Arn: arn:aws:sns:us-east-1:000000000000:local-events
localstack                    | 2024-10-01T01:38:29.997  INFO --- [et.reactor-0] localstack.request.aws     : AWS sns.Subscribe => 200
localstack                    | Subscription Arn: arn:aws:sns:us-east-1:000000000000:local-events:e98cb2ad-565c-4160-a77c-6a4bdd21350f
localstack                    | 2024-10-01T01:38:30.001  INFO --- [et.reactor-0] localstack.request.http    : GET /_aws/sns/subscription-tokens/arn:aws:sns:us-east-1:000000000000:local-events:e98cb2ad-565c-4160-a77c-6a4bdd21350f => 200
localstack                    | Token Res: {'subscription_token': '75732d656173742d312f8c8e1e8b8c8e1e8b8c8e1e8b8c8e1e8b8c8e1e8b8c8e', 'subscription_arn': 'arn:aws:sns:us-east-1:000000000000:local-events:e98cb2ad-565c-4160-a77c-6a4bdd21350f'}
localstack                    | 2024-10-01T01:38:30.004  INFO --- [et.reactor-0] localstack.request.aws     : AWS sns.ConfirmSubscription => 200
localstack                    | Subscription Confirmed: True
localstack                    | Ready.
my-api-1                      | Received event {}

Troubleshooting Steps ~~so far~~

  1. Exec'd into the Localstack container and ran curl -XPOST http://host.docker.internal:3000/events -d '{ "msg": "curl test message" }' I get back the expected response and see the following in the docker logs
my-api-1                      | Received event { '{ "msg": "curl test message" }': '' }
  1. Confirmed the Topic was Subscribed by running awslocal sns get-subscription-attributes --subscription-arn "<subscription-arn-from-logs>" and got back a response with "ConfirmationWasAuthenticated": "true" and "PendingConfirmation": "false"

References

Upvotes: 0

Views: 160

Answers (0)

Related Questions