Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 23 additions & 7 deletions cli/src/pcluster/api/controllers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import List, Optional, Set, Union

import boto3
from botocore.exceptions import ProfileNotFound
from packaging import version as packaging_version

from pcluster.api.errors import (
Expand All @@ -43,14 +44,23 @@ def _set_region(region):
region_backup = os.environ.get("AWS_DEFAULT_REGION")
LOGGER.info("Setting AWS Region to %s", region)
os.environ["AWS_DEFAULT_REGION"] = region
if region not in retrieve_supported_regions():
if region_backup:
os.environ["AWS_DEFAULT_REGION"] = region_backup
else:
del os.environ["AWS_DEFAULT_REGION"]
try:
supported_regions = retrieve_supported_regions()
except ProfileNotFound as e:
_restore_region(region_backup)
raise BadRequestException(str(e))
if region not in supported_regions:
_restore_region(region_backup)
raise BadRequestException(f"invalid or unsupported region '{region}'")


def _restore_region(region_backup):
if region_backup:
os.environ["AWS_DEFAULT_REGION"] = region_backup
else:
del os.environ["AWS_DEFAULT_REGION"]


def configure_aws_region_from_config(region: Union[None, str], config_str: str):
"""Set the region based on either the configuration or theregion parameter."""
# Allow parsing errors to pass through as they will be caught by later functions
Expand All @@ -62,7 +72,10 @@ def configure_aws_region_from_config(region: Union[None, str], config_str: str):
if region and config_region and region != config_region:
raise BadRequestException("region is set in both parameter and configuration and conflicts.")

_set_region(region or config_region or boto3.Session().region_name)
try:
_set_region(region or config_region or boto3.Session().region_name)
except ProfileNotFound as e:
raise BadRequestException(str(e))


def configure_aws_region():
Expand All @@ -77,7 +90,10 @@ def configure_aws_region():
def _decorator_validate_region(func):
@functools.wraps(func)
def _wrapper_validate_region(*args, **kwargs):
_set_region(kwargs.get("region") or boto3.Session().region_name)
try:
_set_region(kwargs.get("region") or boto3.Session().region_name)
except ProfileNotFound as e:
raise BadRequestException(str(e))
return func(*args, **kwargs)

return _wrapper_validate_region
Expand Down
66 changes: 66 additions & 0 deletions cli/tests/pcluster/api/controllers/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import pytest
from assertpy import assert_that, fail
from botocore.exceptions import ProfileNotFound

from pcluster.api.controllers.common import (
assert_supported_operation,
Expand Down Expand Up @@ -116,3 +117,68 @@ def test_assert_supported_operation(mocker, operation, region, expected_support)
assert_that(str(exc.value)).is_equal_to(
f"The operation '{operation.value}' is not supported in region '{region}'."
)


class TestProfileNotFound:
"""Test that ProfileNotFound is handled gracefully instead of causing a fatal exception."""

@pytest.mark.parametrize(
"mock_target, call_func, call_kwargs, expected_profile",
[
# No region provided, boto3.Session() raises ProfileNotFound in decorator
(
"pcluster.api.controllers.common.boto3.Session",
"configure_aws_region_decorator",
{"region": None},
"invalid",
),
# Region provided, retrieve_supported_regions raises ProfileNotFound in _set_region
(
"pcluster.api.controllers.common.retrieve_supported_regions",
"configure_aws_region_decorator",
{"region": "us-east-1"},
"invalid",
),
# No region in config or param, boto3.Session() raises ProfileNotFound in configure_aws_region_from_config
(
"pcluster.api.controllers.common.boto3.Session",
"configure_aws_region_from_config",
{"region": None, "config_str": "Test: asdf"},
"badprofile",
),
# Region in config, retrieve_supported_regions raises ProfileNotFound in _set_region
(
"pcluster.api.controllers.common.retrieve_supported_regions",
"configure_aws_region_from_config",
{"region": None, "config_str": "Region: us-east-1"},
"invalid",
),
],
)
def test_invalid_profile_raises_bad_request(self, mocker, mock_target, call_func, call_kwargs, expected_profile):
mocker.patch(mock_target, side_effect=ProfileNotFound(profile=expected_profile))

with pytest.raises(BadRequestException) as e:
if call_func == "configure_aws_region_decorator":

@configure_aws_region()
def _decorated_func(region=None):
pass

_decorated_func(**call_kwargs)
else:
configure_aws_region_from_config(call_kwargs["region"], call_kwargs["config_str"])

assert_that(str(e.value.content)).contains(expected_profile)
assert_that(str(e.value.content)).contains("could not be found")

def test_valid_profile_works(self):
"""When profile is valid, the decorator works normally."""

@configure_aws_region()
def _decorated_func(region=None):
return "success"

result = _decorated_func(region="eu-west-1")
assert_that(result).is_equal_to("success")
assert_that(os.environ["AWS_DEFAULT_REGION"]).is_equal_to("eu-west-1")
Loading