Skip to content

feat(handler): Add custom header to all AWS responses from LocalStack #12769

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

Merged
merged 10 commits into from
Jun 25, 2025

Conversation

gregfurman
Copy link
Contributor

@gregfurman gregfurman commented Jun 17, 2025

Motivation

This PR adds a custom header to all service responses -- allowing us to distinguish whether a request orignates from LocalStack or AWS.

Changes

  • Adds a LOCALSTACK_RESPONSE_HEADER_ENABLED environment variable that is opt-out by default.
  • When LOCALSTACK_RESPONSE_HEADER_ENABLED is truthy, a handler is added to the response chain that adds a x-localstack: true header to all responses from LocalStack gateway.

@gregfurman gregfurman added this to the 4.6 milestone Jun 17, 2025
@gregfurman gregfurman requested a review from joe4dev June 17, 2025 23:42
@gregfurman gregfurman self-assigned this Jun 17, 2025
@gregfurman gregfurman added the semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases label Jun 17, 2025
Copy link

github-actions bot commented Jun 17, 2025

S3 Image Test Results (AMD64 / ARM64)

    2 files    2 suites   8m 25s ⏱️
  510 tests 460 ✅  50 💤 0 ❌
1 020 runs  920 ✅ 100 💤 0 ❌

Results for commit 6c75361.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Jun 17, 2025

Test Results - Preflight, Unit

21 727 tests  ±0   20 070 ✅ ±0   6m 11s ⏱️ ±0s
     1 suites ±0    1 657 💤 ±0 
     1 files   ±0        0 ❌ ±0 

Results for commit 6c75361. ± Comparison against base commit 3d56935.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Jun 18, 2025

Test Results (amd64) - Acceptance

7 tests  ±0   5 ✅ ±0   3m 7s ⏱️ -1s
1 suites ±0   2 💤 ±0 
1 files   ±0   0 ❌ ±0 

Results for commit 6c75361. ± Comparison against base commit 3d56935.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Jun 18, 2025

Test Results - Alternative Providers

988 tests  +1   622 ✅ +33   34m 23s ⏱️ + 4m 59s
  4 suites ±0   366 💤  - 32 
  4 files   ±0     0 ❌ ± 0 

Results for commit f3f9475. ± Comparison against base commit 67b3da6.

This pull request skips 1 and un-skips 34 tests.
tests.aws.services.cloudformation.v2.ported_from_v1.resources.test_stack_sets ‑ test_create_stack_set_with_stack_instances
tests.aws.services.cloudformation.v2.ported_from_v1.api.test_stacks.TestStacksApi ‑ test_update_stack_with_same_template_withoutchange
tests.aws.services.cloudformation.v2.ported_from_v1.api.test_templates ‑ test_create_stack_from_s3_template_url[http_host]
tests.aws.services.cloudformation.v2.ported_from_v1.api.test_templates ‑ test_create_stack_from_s3_template_url[http_invalid]
tests.aws.services.cloudformation.v2.ported_from_v1.api.test_templates ‑ test_create_stack_from_s3_template_url[http_path]
tests.aws.services.cloudformation.v2.ported_from_v1.api.test_templates ‑ test_create_stack_from_s3_template_url[s3_url]
tests.aws.services.cloudformation.v2.ported_from_v1.engine.test_conditions.TestCloudFormationConditions ‑ test_conditional_in_conditional[dev-us-west-2]
tests.aws.services.cloudformation.v2.ported_from_v1.engine.test_conditions.TestCloudFormationConditions ‑ test_conditional_in_conditional[production-us-east-1]
tests.aws.services.cloudformation.v2.ported_from_v1.engine.test_conditions.TestCloudFormationConditions ‑ test_simple_condition_evaluation_deploys_resource
tests.aws.services.cloudformation.v2.ported_from_v1.engine.test_conditions.TestCloudFormationConditions ‑ test_simple_condition_evaluation_doesnt_deploy_resource
tests.aws.services.cloudformation.v2.ported_from_v1.engine.test_mappings.TestCloudFormationMappings ‑ test_simple_mapping_working
…

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Jun 18, 2025

Test Results (amd64) - Integration, Bootstrap

    5 files      5 suites   2h 20m 51s ⏱️
5 251 tests 4 325 ✅ 926 💤 0 ❌
5 257 runs  4 325 ✅ 932 💤 0 ❌

Results for commit 6c75361.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Jun 18, 2025

LocalStack Community integration with Pro

    2 files  ±0      2 suites  ±0   1h 44m 58s ⏱️ +8s
4 894 tests ±0  4 120 ✅ ±0  774 💤 ±0  0 ❌ ±0 
4 896 runs  ±0  4 120 ✅ ±0  776 💤 ±0  0 ❌ ±0 

Results for commit 6c75361. ± Comparison against base commit 3d56935.

♻️ This comment has been updated with latest results.

@gregfurman gregfurman marked this pull request as ready for review June 18, 2025 07:45
@gregfurman gregfurman requested a review from thrau as a code owner June 18, 2025 07:45
@gregfurman gregfurman force-pushed the add/infr/is-localstack-check branch from e3c1dcf to 0447cfd Compare June 19, 2025 08:31
@gregfurman gregfurman requested review from tiurin and anisaoshafi and removed request for dominikschubert, bentsku and MEPalma June 19, 2025 11:00
Copy link
Contributor

@tiurin tiurin left a comment

Choose a reason for hiding this comment

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

Reviewed and tested env variable in a pairing session.

@@ -1245,6 +1245,9 @@ def use_custom_dns():
# This flag enables `connect_to` to be in-memory only and not do networking calls
IN_MEMORY_CLIENT = is_env_true("IN_MEMORY_CLIENT")

# This flag enables all responses from LocalStack to contain a `x-localstack` HTTP header.
Copy link
Contributor

Choose a reason for hiding this comment

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

dismiss: would rephrase this that flag "disables" this header from being set in response. default: True.

just a heads up to add the env var in public docs: https://docs.localstack.cloud/references/configuration/

@@ -107,6 +108,26 @@ def _infrastructure_setup(
return _infrastructure_setup


@pytest.hookimpl
def pytest_runtest_setup(item):
Copy link
Contributor

Choose a reason for hiding this comment

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

would be nice to add a small high-level docstring/comment what this fixture was added for, so one can understand it in the code without having to open the PR where it was introduced for additional context.

e.g: """ fixture that skips header x-localstack from snapshot evaluation"""


# Otherwise, dynamically inject a path to the custom header inside all skip_snapshot_verify markers paths.
# If a path parameter is None, it's assumed that all paths are skipped.
for mark in skip_markers:
Copy link
Contributor

Choose a reason for hiding this comment

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

question: there are some snapshot tests example where path parameter is not explicitly set, but just the list with the item is passed. Is that covered by these conditions?

@markers.snapshot.skip_snapshot_verify( [ "$..Invalidation.Status", # In real AWS it can be still in progress "$..Location", # not currently supported ] )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Huh that is a good question 🤔 Let me see if the kwargs accounts for this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK damn. You're correct. This is not accounted for. Nice catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Update on this... looks like paths are not skipped when added as args as opposed to kwargs 💀

That means the test you linked actually skips all paths.

Copy link
Contributor

@anisaoshafi anisaoshafi left a comment

Choose a reason for hiding this comment

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

nice and clean, good job @gregfurman 👏🏼
I liked the logic for skipping the x-localstack header check from snapshot matching.

@gregfurman gregfurman requested a review from steffyP as a code owner June 23, 2025 13:13
@gregfurman gregfurman requested review from dfangl and removed request for thrau and steffyP June 23, 2025 13:15
Copy link
Member

@dfangl dfangl left a comment

Choose a reason for hiding this comment

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

I like the new approach way more!
Just one question: Did you run an -ext run as well? And did you check if there are any lists of header keys where this could end up? (your logic only removes them from dicts)

@gregfurman gregfurman force-pushed the add/infr/is-localstack-check branch from f3f9475 to 6c75361 Compare June 25, 2025 09:36
Copy link
Member

@dfangl dfangl left a comment

Choose a reason for hiding this comment

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

LGTM!

@@ -118,6 +118,9 @@ def snapshot(request, _snapshot_session: SnapshotSession, account_id, region_nam
RegexTransformer(f"arn:{get_partition(region_name)}:", "arn:<partition>:"), priority=2
)

# Removes the 'x-localstack' header from all responses
_snapshot_session.add_transformer(_snapshot_session.transform.remove_key("x-localstack"))
Copy link
Member

Choose a reason for hiding this comment

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

Nit: We could reference the constant for the header here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah fair. I originally did this but realised it's more explicit to add the string literal over referencing the constant. Will add in a follow-up.

@gregfurman gregfurman merged commit 25552f2 into master Jun 25, 2025
46 checks passed
@gregfurman gregfurman deleted the add/infr/is-localstack-check branch June 25, 2025 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants