-
Notifications
You must be signed in to change notification settings - Fork 1
S3 localstack ize #266
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
base: master
Are you sure you want to change the base?
S3 localstack ize #266
Changes from 15 commits
9b4b6ab
17551d3
6dc574a
d136776
9649ea6
c33cabd
436ddd3
0051a6e
2ebe024
1382272
25fcce1
8fb480b
3cd25c5
ebfa631
024c6b4
7e2f6f0
11d0209
9716414
fd1ed9a
efe3495
fcbe1f8
48f5860
11d881c
f537d44
b6a11ae
3b7f212
a22ce88
1942232
9f59c2e
2ae8317
9c20065
d0f5a5a
daad33e
2a84d18
fc91806
7387f0f
05d78e6
0b37679
646a265
0f3d3f0
6eecccf
8f7cb75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import boto_monkey_patching # noqa |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # Module to monkey patch the boto3 client and resource functions to use a custom endpoint-url. | ||
| # Originally introduced June 2023 for overriding certain boto3 services (e.g. s3, sqs) to use the | ||
| # localstack utility, which provides a way to run some AWS services locally, for testing purposes. | ||
| # Currently only supported for S3 and SQS. To use this set the environment variables LOCALSTACK_S3_URL | ||
| # and/or LOCALSTACK_SQS_URL to the localstack URL, for example, http://localhost:4566. | ||
| # Reference: https://localstack.cloud | ||
|
|
||
| import boto3 | ||
| import os | ||
| from typing import Optional | ||
|
|
||
|
|
||
| _boto_client_original = boto3.client | ||
| _boto_resource_original = boto3.resource | ||
| _boto_service_overrides_supported = [ | ||
| {"service": "s3", "env": "LOCALSTACK_S3_URL"}, | ||
| {"service": "sqs", "env": "LOCALSTACK_SQS_URL"} | ||
| ] | ||
| # This will entirely disable this feature; for troubleshooting only. | ||
| _boto_monkey_patching_disabled = False | ||
|
|
||
| # For import only in test_boto_monkey_patching. | ||
| _boto_monkey_patching_services = [item["service"] for item in _boto_service_overrides_supported] | ||
|
|
||
|
|
||
| # For import only in test_boto_monkey_patching. | ||
| def _boto_monkey_patching_endpoint_url_environ_name(service: str) -> Optional[str]: | ||
| for item in _boto_service_overrides_supported: | ||
| if item["service"] == service: | ||
| return item["env"] | ||
| return None | ||
|
|
||
|
|
||
| def _setup_monkey_patching_kwargs(*args, **kwargs) -> dict: | ||
| if not _boto_monkey_patching_disabled: | ||
| endpoint_url = kwargs.get("endpoint_url") | ||
| if not endpoint_url: | ||
| for service_override in _boto_service_overrides_supported: | ||
| if service_override["service"] in args: | ||
| endpoint_url = os.environ.get(service_override["env"]) | ||
| if endpoint_url: | ||
| kwargs["endpoint_url"] = endpoint_url | ||
| break | ||
| return kwargs | ||
|
|
||
|
|
||
| def _monkey_patched_boto_client(*args, **kwargs): | ||
| kwargs = _setup_monkey_patching_kwargs(*args, **kwargs) | ||
| return _boto_client_original(*args, **kwargs) | ||
|
|
||
|
|
||
| def _monkey_patched_boto_resource(*args, **kwargs): | ||
| kwargs = _setup_monkey_patching_kwargs(*args, **kwargs) | ||
| return _boto_resource_original(*args, **kwargs) | ||
|
|
||
|
|
||
| boto3.client = _monkey_patched_boto_client | ||
| boto3.resource = _monkey_patched_boto_resource | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,11 @@ | ||
| import os | ||
|
|
||
| # Disable any AWS endpoint-url environment variables overrides for testing. | ||
| if "S3_URL" in os.environ: | ||
| os.environ.pop("S3_URL") | ||
| if "SQS_URL" in os.environ: | ||
| os.environ.pop("SQS_URL") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this not globally eliminate these values? You probably want a session scoped fixture that pops these values back on and off, just in case? Though probably not a big deal in local environment.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I was a little unsure of this - was doing this because when I had this env vars set in my env, because I was using this facility for testing locally with localstack, tests were failing (because tests not setup of course with the assumption of using localstack), so I just globally unset these variables for all testing. |
||
|
|
||
| import pytest | ||
| import requests | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import boto3 | ||
| import os | ||
| from typing import Optional | ||
| from dcicutils.misc_utils import override_environ | ||
|
|
||
| # Reach into the implementation details of the boto_monkey_patching module | ||
| # to get list of boto3 services for which we support monkey patching, and to | ||
| # get the environment variable name used to do the associated endpoint-url override. | ||
| from dcicutils.boto_monkey_patching import _boto_monkey_patching_services | ||
| from dcicutils.boto_monkey_patching import _boto_monkey_patching_endpoint_url_environ_name | ||
|
|
||
|
|
||
| _override_endpoint_url = "http://localhost:4566" | ||
|
|
||
|
|
||
| def _is_default_aws_endpoint_url(endpoint_url: str, service: str): | ||
| return (endpoint_url == f"https://{service}.amazonaws.com" or | ||
| endpoint_url == f"https://{service}.{os.environ.get('AWS_DEFAULT_REGION')}.amazonaws.com") | ||
|
|
||
|
|
||
| def _environ_overrides(service: str, endpoint_url: Optional[str]) -> dict: | ||
| return { | ||
| _boto_monkey_patching_endpoint_url_environ_name(service): endpoint_url, | ||
| "AWS_DEFAULT_REGION": "us-east-1" | ||
| } | ||
|
|
||
|
|
||
| def _test_boto_monkey_patching_client_without_overriding(service: str): | ||
| with override_environ(**_environ_overrides(service, None)): | ||
| s3_client = boto3.client(service) | ||
| assert _is_default_aws_endpoint_url(s3_client.meta.endpoint_url, service) | ||
|
|
||
|
|
||
| def _test_boto_monkey_patching_resource_without_overriding(service: str): | ||
| with override_environ(**_environ_overrides(service, None)): | ||
| s3_resource = boto3.resource(service) | ||
| assert _is_default_aws_endpoint_url(s3_resource.meta.client._endpoint.host, service) | ||
|
|
||
|
|
||
| def _test_boto_monkey_patching_client_with_overriding(service: str): | ||
| with override_environ(**_environ_overrides(service, _override_endpoint_url)): | ||
| s3_client = boto3.client(service) | ||
| assert s3_client.meta.endpoint_url == _override_endpoint_url | ||
|
|
||
|
|
||
| def _test_boto_monkey_patching_resource_with_overriding(service: str): | ||
| with override_environ(**_environ_overrides(service, _override_endpoint_url)): | ||
| s3_resource = boto3.resource(service) | ||
| assert s3_resource.meta.client._endpoint.host == _override_endpoint_url | ||
|
|
||
|
|
||
| def test_boto_monkey_patching_client_without_overriding(): | ||
| for service in _boto_monkey_patching_services: | ||
| _test_boto_monkey_patching_client_without_overriding(service) | ||
|
|
||
|
|
||
| def test_boto_monkey_patching_resource_without_overriding(): | ||
| for service in _boto_monkey_patching_services: | ||
| _test_boto_monkey_patching_resource_without_overriding(service) | ||
|
|
||
|
|
||
| def test_boto_monkey_patching_client_with_overriding(): | ||
| for service in _boto_monkey_patching_services: | ||
| _test_boto_monkey_patching_client_with_overriding(service) | ||
|
|
||
|
|
||
| def test_boto_monkey_patching_resource_with_overriding(): | ||
| for service in _boto_monkey_patching_services: | ||
| _test_boto_monkey_patching_resource_with_overriding(service) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want constant for
LOCALSTACK_S3_URLetc so can be imported and patched from pytest easilyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, thanks.