Skip to content

Sns topic update functionality #12777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

michalisfot
Copy link

@michalisfot michalisfot commented Jun 19, 2025

Related PR: #12768

Motivation

This PR implements basic update support for the AWS::SNS::Topic CloudFormation resource, addressing the core requirement to enable updates for DisplayName, TopicName, and Tags properties. The implementation follows AWS CloudFormation's update semantics, including resource replacement when immutable properties change.

Changes

Enhanced Resource Provider (aws_sns_topic.py)

  • Implemented complete update() method with support for:

    • In-place updates: DisplayName attribute changes
    • Resource replacement: TopicName changes (recreates topic with new name)
    • Tag management: Add/remove/update tags with proper diffing
    • State preservation: Maintains subscriptions and existing tags during topic replacement
  • Key features:

    • Proper ARN handling and validation
    • Graceful error handling for subscription/tag preservation
    • Follows AWS update semantics (replacement vs. in-place)
    • Resource cleanup (deletes old topic after successful replacement)

Comprehensive Test Coverage (test_sns.py)

  • Created test_sns_topic_update_attributes() with in-place updates (no resource replacement):

    • DisplayName updates: Verifies DisplayName attribute changes are applied correctly
    • Tag updates: Tests adding, modifying, and removing tags
    • Same topic preservation: Ensures TopicArn remains unchanged during in-place updates
    • Snapshot validation: Captures before/after state for attribute and tag changes
  • Created test_sns_topic_update_name() with resource replacement:

    • Tag preservation validation: Verifies existing tags are preserved during replacement
    • Subscription preservation validation: Ensures subscriptions are migrated to new topic
    • ARN format validation: Builds and validates new ARN using proper AWS format
    • Resource replacement verification: Confirms old topic deletion and new topic creation
    • Comprehensive snapshots: Captures before/after state for all operations

Added new entries to the Snapshot File (test_sns.snapshot.json)

Testing

  • On AWS and ocally both test pass.

TODO

  • Test against AWS and find out why test_sns_topic_update_name test fails on AWS but not locally.
  • Find a proper way to validate the snapshot against the tags rather than manually checking each tag

Proposed Next Steps

1: Complete Core Properties

  1. Archive Policy Support
  2. Access Policy Support
  3. KMS Key Management
  4. FIFO Topic Properties
  5. ContentBasedDeduplication

2: Advanced Update Capabilities

  1. Subscription Management
  2. Data Protection Policy
  3. Delivery Policy Configuration

3: Robustness & Edge Cases

  1. Error Handling Enhancement - Improve rollback scenarios for failed updates
  2. Performance Optimizations

4: Testing

  1. Multi account and region testing
  2. Other types of testing as mentioned in the docs

@localstack-bot
Copy link
Collaborator

localstack-bot commented Jun 19, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@simonrw simonrw added the semver: patch Non-breaking changes which can be included in patch releases label Jun 19, 2025
@michalisfot
Copy link
Author

recheck

1 similar comment
@michalisfot
Copy link
Author

recheck

@michalisfot
Copy link
Author

I have read the CLA Document and I hereby sign the CLA.

@alexrashed
Copy link
Member

recheck

localstack-bot added a commit that referenced this pull request Jun 23, 2025
Copy link
Contributor

@simonrw simonrw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for posting the updates to this PR. I have left some follow ups on the previous PR and added some comments here. In general I am ok with this PR. I think we have captured a good part of the update implementation for SNS topics, including using CFn to manage the subscriptions.

I would be interested to capture the behaviour you had previously in a follow up where the subscriptions are created outside of CFn, however I think this use case is less common and therefore not as high a priority as the changes you've added here.

Thanks very much!

(I have requested changes again as I still have a couple of outstanding comments that have not been addressed. These are only minor though, I just would like your take on them before I approve).

"$..Attributes.Policy.Statement..Action", # SNS:Receive is added by moto but not returned in AWS
]
)
def test_sns_topic_update_attributes(deploy_cfn_template, aws_client, snapshot):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the shape of this test looks fine.

suggestion: perhaps consider validating the subscriptions as you do in the other test? Changing these attributes (that don't require replacement) should mean a subscription remains "attached", right?

nit: if a Python object (e.g. dictionary) is compared with snapshot.match you don't need to assert the individual fields of that object

Comment on lines +233 to +235
tag_dict = {tag["Key"]: tag["Value"] for tag in initial_tags["Tags"]}
assert tag_dict["Environment"] == "test"
assert tag_dict["Project"] == "localstack"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tag_dict = {tag["Key"]: tag["Value"] for tag in initial_tags["Tags"]}
assert tag_dict["Environment"] == "test"
assert tag_dict["Project"] == "localstack"
snapshot.match("initial-tags", initial_tags)

We may need to add specific non-supported tags to the skip_snapshot_verify decorator at the top of the test function, as CFn adds its own tags to resources and we don't support that yet.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my initial plan. I couldn't make it work though because of how the Tags are formatted. I couldn't find a way to skip the verification based on the Key. Also, AWS creates the tags in a random order and this also affects the validation. My idea was to temporarily check the tags manually until we find a proper solution (which might be already there but I missed it). I checked other examples but the tags there were formatted differently e.g. in Lambda snapshots tags are all in a single dictionary (not a list of dicts like bellow).

E.g.:

        "Tags": [
          {
            "Key": "Project",
            "Value": "localstack"
          },
          {
            "Key": "aws:cloudformation:stack-name",
            "Value": "replaced-value"
          },
          {
            "Key": "Environment",
            "Value": "test"
          },
          {
            "Key": "aws:cloudformation:logical-id",
            "Value": "TestTopic"
          },
          {
            "Key": "aws:cloudformation:stack-id",
            "Value": "replaced-value"
          }
        ],

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fair enough for now. Thanks for explaining.

Comment on lines +258 to +260
updated_tag_dict = {tag["Key"]: tag["Value"] for tag in updated_tags["Tags"]}
assert updated_tag_dict["Environment"] == "production"
assert updated_tag_dict["Project"] == "backend"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
updated_tag_dict = {tag["Key"]: tag["Value"] for tag in updated_tags["Tags"]}
assert updated_tag_dict["Environment"] == "production"
assert updated_tag_dict["Project"] == "backend"
snapshot.match("updated-tags", updated_tags)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert updated_tag_dict["Project"] == "backend"

# Subscriptions should be preserved
snapshot.match("new-subscriptions", initial_subscriptions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: you are not refetching the subscriptions here, you are asserting the contents of the old subscriptions response. Can you update this snapshot to fetch the new subscriptions?

Suggested change
snapshot.match("new-subscriptions", initial_subscriptions)
snapshot.match("new-subscriptions", initial_subscriptions)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was done on purpose. I though that if the initial_subscriptions match the new subscriptions on the snapshot it means they are preserved. Do you mean we should do something like this?

    new_subscriptions = aws_client.sns.list_subscriptions_by_topic(TopicArn=topic_arn)
    snapshot.match("new-subscriptions", new_subscriptions)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is what's required. You have already snapshotted the initial subscriptions, and the value of the variable does not change between your two snapshot.match calls. Therefore you are snapshotting the exact same information twice.

The snapshot library takes care of what the expected value should be. Provided you have captured the snapshot against AWS and the tests pass on LocalStack, we have confidence that the responses and behaviour match.

So you first get the subscriptions for the topic on create, then update the topic and re-fetch the subscriptions after the update. Whether they match is irrelevant, the fact that the snapshots are the same means that LocalStack does the same as what AWS does.

"HTTPStatusCode": 200
}
},
"initial-topic-tags": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These keys are not in the test anywhere, perhaps you need to regenerate your snapshots (after addressing my comments above).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: this change is unrelated to this PR, can we undo this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, it got away while testing. I'll roll it back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver: patch Non-breaking changes which can be included in patch releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants