Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9b4b6ab
Factor on boto3 s3/sqs client/resource creation for optional override…
dmichaels-harvard Jun 16, 2023
17551d3
Version update
dmichaels-harvard Jun 23, 2023
6dc574a
Version on CHANGES.rst updates.
dmichaels-harvard Jun 29, 2023
d136776
boto3 imports
dmichaels-harvard Jun 29, 2023
9649ea6
Suppport for monkey patched version of localstack-ization of boto3 s3…
dmichaels-harvard Jun 29, 2023
c33cabd
Suppport for monkey patched version of localstack-ization of boto3 s3…
dmichaels-harvard Jun 29, 2023
436ddd3
Suppport for monkey patched version of localstack-ization of boto3 s3…
dmichaels-harvard Jun 29, 2023
0051a6e
Suppport for monkey patched version of localstack-ization of boto3 s3…
dmichaels-harvard Jun 29, 2023
2ebe024
Suppport for monkey patched version of localstack-ization of boto3 s3…
dmichaels-harvard Jun 29, 2023
1382272
flake8
dmichaels-harvard Jun 29, 2023
25fcce1
Suppport for monkey patched version of localstack-ization of boto3 s3…
dmichaels-harvard Jun 29, 2023
8fb480b
Debugging publish
dmichaels-harvard Jun 29, 2023
3cd25c5
Debugging publish
dmichaels-harvard Jun 29, 2023
ebfa631
Debugging publish
dmichaels-harvard Jun 29, 2023
024c6b4
Changed boto3 localstack monkeypatching to use LOCALSTACK_S3_URL and …
dmichaels-harvard Jul 5, 2023
7e2f6f0
Added some notes on localstack usage in getting_started.rst and added…
dmichaels-harvard Jul 5, 2023
11d0209
Added some notes on localstack usage in getting_started.rst and added…
dmichaels-harvard Jul 5, 2023
9716414
Merge from master
dmichaels-harvard Jul 5, 2023
fd1ed9a
Fix for (smaht) for test_common.py - this fix needs to go in master too
dmichaels-harvard Jul 5, 2023
efe3495
Merge branch 'master' into s3-localstack-ize
dmichaels-harvard Jul 7, 2023
fcbe1f8
merge from master
dmichaels-harvard Jul 25, 2023
48f5860
merge from master
dmichaels-harvard Aug 7, 2023
11d881c
merge from master
dmichaels-harvard Aug 7, 2023
f537d44
merge from master
dmichaels-harvard Aug 7, 2023
b6a11ae
merge from master
dmichaels-harvard Aug 7, 2023
3b7f212
merge from master
dmichaels-harvard Aug 7, 2023
a22ce88
merge in kmp_fix_broken_project_utils_test branch with test fix
dmichaels-harvard Aug 8, 2023
1942232
Merge branch 'master' into s3-localstack-ize
dmichaels-harvard Aug 8, 2023
9f59c2e
Merge branch 'kmp_add_dns_licenses' into s3-localstack-ize
dmichaels-harvard Aug 8, 2023
2ae8317
Added localstack-ext (Apache-2.0) in license exception table.
dmichaels-harvard Aug 8, 2023
9c20065
merge from master
dmichaels-harvard Aug 11, 2023
d0f5a5a
typo
dmichaels-harvard Aug 11, 2023
daad33e
white space fix
dmichaels-harvard Aug 11, 2023
2a84d18
Minor comment/typo cleanup in publish_to_pypi.py
dmichaels-harvard Aug 12, 2023
fc91806
Update PyYAML to ^6.0.1; Mac M1 with Python 3.9 requires 5.3.1 (not 5…
dmichaels-harvard Aug 13, 2023
7387f0f
Comments
dmichaels-harvard Aug 13, 2023
05d78e6
Merge from master
dmichaels-harvard Aug 22, 2023
0b37679
merge from master
dmichaels-harvard Aug 25, 2023
646a265
CHANGELOG.rst updates.
dmichaels-harvard Aug 25, 2023
0f3d3f0
Fix to test_s3_utils from kmp_sheet_utils_schema_hinting branch.
dmichaels-harvard Aug 25, 2023
6eecccf
flake8 update
dmichaels-harvard Aug 25, 2023
8f7cb75
comment
dmichaels-harvard Sep 5, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/main-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
with:
python-version: 3.9
- name: Install Python dependencies for publish
run: pip install requests toml
run: pip install boto3 requests toml
- name: Publish
env:
PYPI_USER: ${{ secrets.PYPI_USER }}
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ dcicutils
Change Log
----------

7.6.0
=====
* Added ``boto_monkey_patching`` module to use monkey patching to override the endpoint URLs for
for S3 or SQS boto3 client/resource creation using the LOCALSTACK_S3_URL or LOCALSTACK_SQS_URL
environment variables to specify that these services should use a locally running ersatz
instance of S3 or SQS via localstack.


7.5.2
=====

Expand Down
1 change: 1 addition & 0 deletions dcicutils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import boto_monkey_patching # noqa
58 changes: 58 additions & 0 deletions dcicutils/boto_monkey_patching.py
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"}

Copy link
Copy Markdown
Member

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_URL etc so can be imported and patched from pytest easily

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, thanks.

]
# 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
7 changes: 7 additions & 0 deletions docs/source/dcicutils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ beanstalk_utils
:members:


boto_monkey_patching
^^^^^^^^^^^^^^^^^^^^

.. automodule:: dcicutils.boto_monkey_patching
:members:


codebuild_utils
^^^^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dcicutils"
version = "7.5.2"
version = "7.6.0.1b5" # TODO: To become 7.6.0
description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources"
authors = ["4DN-DCIC Team <support@4dnucleome.org>"]
license = "MIT"
Expand Down
7 changes: 7 additions & 0 deletions test/conftest.py
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")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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

Expand Down
69 changes: 69 additions & 0 deletions test/test_boto_monkey_patching.py
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)