diff --git a/.changes/next-release/bugfix-IMDS-51328.json b/.changes/next-release/bugfix-IMDS-51328.json new file mode 100644 index 000000000000..38b5467c833e --- /dev/null +++ b/.changes/next-release/bugfix-IMDS-51328.json @@ -0,0 +1,5 @@ +{ + "type": "bugfix", + "category": "IMDS", + "description": "Fixes region resolution from IMDS in Local Zones by reading the region directly instead of deriving it from the availability zone." +} diff --git a/awscli/utils.py b/awscli/utils.py index 270d98fdfe1f..543f3a30a0f7 100644 --- a/awscli/utils.py +++ b/awscli/utils.py @@ -163,7 +163,7 @@ def _create_fetcher(self): class InstanceMetadataRegionFetcher(IMDSFetcher): - _URL_PATH = 'latest/meta-data/placement/availability-zone/' + _URL_PATH = 'latest/meta-data/placement/region' def retrieve_region(self): """Get the current region from the instance metadata service. @@ -204,9 +204,7 @@ def _get_region(self): retry_func=self._default_retry, token=token, ) - availability_zone = response.text - region = availability_zone[:-1] - return region + return response.text def split_on_commas(value): diff --git a/scripts/new-change b/scripts/new-change index dc0b69893904..cb644ea4a8ae 100755 --- a/scripts/new-change +++ b/scripts/new-change @@ -29,7 +29,7 @@ You'll see this template:: Fill in the appropriate values, save and exit the editor. Make sure to commit these changes as part of your pull request. -If, when your editor is open, you decide don't don't want to add a changelog +If, when your editor is open, you decide you don't want to add a changelog entry, save an empty file and no entry will be generated. You can then use the ``scripts/render-change`` to generate the diff --git a/tests/functional/test_clidriver.py b/tests/functional/test_clidriver.py index 4e3730a731d0..11f18c964bac 100644 --- a/tests/functional/test_clidriver.py +++ b/tests/functional/test_clidriver.py @@ -66,9 +66,8 @@ def test_imds_region_is_used_as_fallback_wo_v2_support(self): # First response should be from the IMDS server for security token # if server supports IMDSv1 only there will be no response for token self.add_response(None) - # Then another response from the IMDS server for an availability - # zone. - self.add_response(b'us-mars-2a') + # Then another response from the IMDS server for the region. + self.add_response(b'us-mars-2') # Once a region is fetched form the IMDS server we need to mock an # XML response from ec2 so that the CLI driver doesn't throw an error # during parsing. @@ -84,9 +83,8 @@ def test_imds_region_is_used_as_fallback_with_v2_support(self): # First response should be from the IMDS server for security token # if server supports IMDSv2 it'll return token self.add_response(b'token') - # Then another response from the IMDS server for an availability - # zone. - self.add_response(b'us-mars-2a') + # Then another response from the IMDS server for the region. + self.add_response(b'us-mars-2') # Once a region is fetched form the IMDS server we need to mock an # XML response from ec2 so that the CLI driver doesn't throw an error # during parsing. diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index a08a085a7bfd..baf095f3ed86 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -399,7 +399,7 @@ def setUp(self): self._send = self._urllib3_patch.start() self._imds_responses = [] self._send.side_effect = self.get_imds_response - self._region = 'us-mars-1a' + self._region = 'us-mars-1' self.environ = {} self.environ_patch = mock.patch('os.environ', self.environ) self.environ_patch.start() @@ -473,6 +473,26 @@ def test_includes_user_agent_header(self): headers = self._send.call_args[0][0].headers self.assertEqual(headers['User-Agent'], user_agent) + def test_requests_region_from_placement_region(self): + self.add_imds_token_response() + self.add_get_region_imds_response() + + InstanceMetadataRegionFetcher().retrieve_region() + + url = self._send.call_args[0][0].url + self.assertTrue(url.endswith('latest/meta-data/placement/region')) + + def test_local_zone_returns_parent_region(self): + # In a Local Zone the availability zone is e.g. us-east-1-atl-2a, + # so the region has to come from placement/region rather than + # being derived from the availability zone. + self.add_imds_token_response() + self.add_get_region_imds_response(region='us-east-1') + + result = InstanceMetadataRegionFetcher().retrieve_region() + + self.assertEqual(result, 'us-east-1') + def test_non_200_response_for_region_is_retried(self): # Response for role name that have a non 200 status code should # be retried.