diff --git a/botocove/cove_host_account.py b/botocove/cove_host_account.py index 403f63a..6e1b664 100644 --- a/botocove/cove_host_account.py +++ b/botocove/cove_host_account.py @@ -59,7 +59,7 @@ def __init__( self.host_account_partition = caller_id["Arn"].split(":")[1] if regions is None: - self.target_regions = [None] + self.target_regions = [assuming_session.region_name] else: self.target_regions = regions diff --git a/pyproject.toml b/pyproject.toml index 81f5d42..2fcfbff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,8 @@ flake8-print = ">=5.0.0" mypy = ">=1.5.1" pre-commit = ">=3.4.0" flakeheaven = ">=3.3.0" -moto = {extras = ["organizations", "sts"], version = ">=4.2.5"} +moto = {extras = ["organizations", "sts", "ec2"], version = ">=4.2.5"} +boto3-stubs = {extras = ["ec2"], version = "*"} pytest-randomly = ">=3.15.0" # These are the last versions compatible with flake8 4. flakeheaven 3.3.0 is diff --git a/tests/conftest.py b/tests/conftest.py index 5d6326e..90453e3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,16 @@ def _clean_env(monkeypatch: MonkeyPatch) -> None: monkeypatch.setenv(env_var, "broken_not_real_profile") +@pytest.fixture(autouse=True) +def _default_region(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setenv("AWS_DEFAULT_REGION", "eu-west-1") + + +@pytest.fixture() +def _no_default_region(monkeypatch: MonkeyPatch) -> None: + monkeypatch.delenv("AWS_DEFAULT_REGION") + + @pytest.fixture() def mock_session() -> Iterator[Session]: """Returns a session with mock AWS services.""" diff --git a/tests/test_assuming_session.py b/tests/test_assuming_session.py new file mode 100644 index 0000000..4083a7a --- /dev/null +++ b/tests/test_assuming_session.py @@ -0,0 +1,53 @@ +import pytest +from boto3.session import Session +from botocore.exceptions import NoRegionError +from moto import mock_ec2 + +from botocove import cove + +# Query the region with different configurations of assuming session and +# environment variables. To make the assertions easier all `cove` calls set +# `raise_exception`. If set the assuming session region and the default region +# are always distinct to be able to assert the source of the query result. + +def _query_region(session: Session) -> str: + with mock_ec2(): + response = session.client("ec2").describe_availability_zones() + return response["AvailabilityZones"][0]["RegionName"] + + +@pytest.fixture(autouse=True) +def _org_with_one_member(mock_session: Session) -> None: + org_client = mock_session.client("organizations") + org_client.create_organization(FeatureSet="ALL") + org_client.create_account(Email="account1@aws.com", AccountName="Account 1") + + +@pytest.mark.usefixtures("_no_default_region") +def test_when_no_assuming_session_and_no_default_region_then_cove_raises_error() -> None: # noqa: 501 + with pytest.raises(NoRegionError, match=r"^You must specify a region\.$"): + cove(_query_region, raise_exception=True)() + + +def test_when_no_assuming_session_then_cove_uses_default_region() -> None: + output = cove(_query_region, raise_exception=True)() + assert output["Results"][0]["Result"] == "eu-west-1" + + +@pytest.mark.usefixtures("_no_default_region") +def test_when_no_default_region_then_cove_uses_assuming_session_region() -> None: + output = cove( + _query_region, + assuming_session=Session(region_name="eu-central-1"), + raise_exception=True, + )() + assert output["Results"][0]["Result"] == "eu-central-1" + + +def test_cove_prefers_assuming_session_region() -> None: + output = cove( + _query_region, + assuming_session=Session(region_name="eu-central-1"), + raise_exception=True, + )() + assert output["Results"][0]["Result"] == "eu-central-1" diff --git a/tests/test_decorator.py b/tests/test_decorator.py index 650f2ec..ce1bf08 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -86,6 +86,7 @@ def simple_func(session: CoveSession, arg1: int, arg2: int, arg3: int) -> int: "Result": 6, "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", + "Region": "eu-west-1", } ] diff --git a/tests/test_decorator_no_args.py b/tests/test_decorator_no_args.py index 5fe4f89..71985f9 100644 --- a/tests/test_decorator_no_args.py +++ b/tests/test_decorator_no_args.py @@ -35,6 +35,7 @@ def simple_func(session: CoveSession) -> str: "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", "Result": "hello", + "Region": "eu-west-1", } ] assert cove_output["Results"] == expected @@ -58,6 +59,7 @@ def simple_func(session: CoveSession, output: str) -> str: "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", "Result": "blue", + "Region": "eu-west-1", } ] assert cove_output["Results"] == expected @@ -86,6 +88,7 @@ def simple_func( "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", "Result": ("blue", "circle", "11:11"), + "Region": "eu-west-1", } ] assert cove_output == expected diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 0a66f97..7143579 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -56,6 +56,7 @@ def simple_func(session: CoveSession) -> None: "Status": "ACTIVE", "RoleSessionName": "OrganizationAccountAccessRole", "ExceptionDetails": repr(Exception("oh no")), + "Region": "eu-west-1", } # Compare repr of exceptions diff --git a/tests/test_regions.py b/tests/test_regions.py index 02d7719..40fd4eb 100644 --- a/tests/test_regions.py +++ b/tests/test_regions.py @@ -5,7 +5,7 @@ from tests.moto_mock_org.moto_models import SmallOrg -def test_when_region_is_unspecified_then_result_has_no_region_key( +def test_when_region_is_unspecified_then_result_has_default_region_key( mock_small_org: SmallOrg, ) -> None: @cove() @@ -15,7 +15,7 @@ def do_nothing(session: Session) -> None: output = do_nothing() assert output["Results"] for result in output["Results"]: - assert "Region" not in result + assert result["Region"] == "eu-west-1" def test_when_region_is_unspecified_then_output_has_one_result_per_account( @@ -26,20 +26,16 @@ def do_nothing(session: Session) -> None: pass output = do_nothing() - print(output["Results"]) - print(len(output["Results"])) - print(_count_member_accounts(mock_session)) - print(mock_small_org.all_accounts) assert len(output["Results"]) == _count_member_accounts(mock_session) def test_when_region_is_str_then_raises_type_error(mock_small_org: SmallOrg) -> None: - @cove(regions="eu-west-1") # type: ignore[arg-type] + @cove(regions="eu-central-1") # type: ignore[arg-type] def do_nothing() -> None: pass with pytest.raises( - TypeError, match=r"regions must be a list of str\. Got str 'eu-west-1'\." + TypeError, match=r"regions must be a list of str\. Got str 'eu-central-1'\." ): do_nothing() @@ -58,20 +54,20 @@ def do_nothing() -> None: def test_when_any_region_is_passed_then_result_has_region_key( mock_small_org: SmallOrg, ) -> None: - @cove(regions=["eu-west-1"]) + @cove(regions=["eu-central-1"]) def do_nothing(session: Session) -> None: pass output = do_nothing() assert output["Results"] for result in output["Results"]: - assert result["Region"] == "eu-west-1" + assert result["Region"] == "eu-central-1" def test_when_two_regions_are_passed_then_output_has_one_result_per_account_per_region( mock_session: Session, mock_small_org: SmallOrg ) -> None: - @cove(regions=["eu-west-1", "us-east-1"]) + @cove(regions=["eu-central-1", "us-east-1"]) def do_nothing(session: Session) -> None: pass @@ -79,7 +75,7 @@ def do_nothing(session: Session) -> None: number_of_member_accounts = _count_member_accounts(mock_session) - for region in ["eu-west-1", "us-east-1"]: + for region in ["eu-central-1", "us-east-1"]: number_of_results_per_region = sum( 1 for result in output["Results"] if result["Region"] == region ) diff --git a/tests/test_session.py b/tests/test_session.py index 4d609aa..2626637 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -37,6 +37,7 @@ def simple_func(session: CoveSession, a_string: str) -> str: "Result": "test-string", "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", + "Region": "eu-west-1", } ] assert cove_output["Results"] == expected @@ -66,6 +67,7 @@ def simple_func(session: CoveSession, a_string: str) -> str: "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", "Policy": session_policy, + "Region": "eu-west-1", } ] assert cove_output["Results"] == expected @@ -97,6 +99,7 @@ def simple_func(session: CoveSession, a_string: str) -> str: "RoleName": "OrganizationAccountAccessRole", "RoleSessionName": "OrganizationAccountAccessRole", "PolicyArns": session_policy_arns, + "Region": "eu-west-1", } ] assert cove_output["Results"] == expected