codekingpro/portable-devtools
114k
1# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.2#3# Licensed under the Apache License, Version 2.0 (the "License"). You4# may not use this file except in compliance with the License. A copy of5# the License is located at6#7# http://aws.amazon.com/apache2.0/8#9# or in the "license" file accompanying this file. This file is10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF11# ANY KIND, either express or implied. See the License for the specific12# language governing permissions and limitations under the License.13"""Resolves regions and endpoints.14 15This module implements endpoint resolution, including resolving endpoints for a16given service and region and resolving the available endpoints for a service17in a specific AWS partition.18"""19import copy20import logging21import re22from enum import Enum23 24from botocore import UNSIGNED, xform_name25from botocore.auth import AUTH_TYPE_MAPS, HAS_CRT26from botocore.crt import CRT_SUPPORTED_AUTH_TYPES27from botocore.endpoint_provider import EndpointProvider28from botocore.exceptions import (29 EndpointProviderError,30 EndpointVariantError,31 InvalidEndpointConfigurationError,32 InvalidHostLabelError,33 MissingDependencyException,34 NoRegionError,35 ParamValidationError,36 UnknownEndpointResolutionBuiltInName,37 UnknownRegionError,38 UnknownSignatureVersionError,39 UnsupportedS3AccesspointConfigurationError,40 UnsupportedS3ConfigurationError,41 UnsupportedS3ControlArnError,42 UnsupportedS3ControlConfigurationError,43)44from botocore.utils import ensure_boolean, instance_cache45 46LOG = logging.getLogger(__name__)47DEFAULT_URI_TEMPLATE = '{service}.{region}.{dnsSuffix}' # noqa48DEFAULT_SERVICE_DATA = {'endpoints': {}}49 50 51class BaseEndpointResolver:52 """Resolves regions and endpoints. Must be subclassed."""53 54 def construct_endpoint(self, service_name, region_name=None):55 """Resolves an endpoint for a service and region combination.56 57 :type service_name: string58 :param service_name: Name of the service to resolve an endpoint for59 (e.g., s3)60 61 :type region_name: string62 :param region_name: Region/endpoint name to resolve (e.g., us-east-1)63 if no region is provided, the first found partition-wide endpoint64 will be used if available.65 66 :rtype: dict67 :return: Returns a dict containing the following keys:68 - partition: (string, required) Resolved partition name69 - endpointName: (string, required) Resolved endpoint name70 - hostname: (string, required) Hostname to use for this endpoint71 - sslCommonName: (string) sslCommonName to use for this endpoint.72 - credentialScope: (dict) Signature version 4 credential scope73 - region: (string) region name override when signing.74 - service: (string) service name override when signing.75 - signatureVersions: (list<string>) A list of possible signature76 versions, including s3, v4, v2, and s3v477 - protocols: (list<string>) A list of supported protocols78 (e.g., http, https)79 - ...: Other keys may be included as well based on the metadata80 """81 raise NotImplementedError82 83 def get_available_partitions(self):84 """Lists the partitions available to the endpoint resolver.85 86 :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]).87 """88 raise NotImplementedError89 90 def get_available_endpoints(91 self, service_name, partition_name='aws', allow_non_regional=False92 ):93 """Lists the endpoint names of a particular partition.94 95 :type service_name: string96 :param service_name: Name of a service to list endpoint for (e.g., s3)97 98 :type partition_name: string99 :param partition_name: Name of the partition to limit endpoints to.100 (e.g., aws for the public AWS endpoints, aws-cn for AWS China101 endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.102 103 :type allow_non_regional: bool104 :param allow_non_regional: Set to True to include endpoints that are105 not regional endpoints (e.g., s3-external-1,106 fips-us-gov-west-1, etc).107 :return: Returns a list of endpoint names (e.g., ["us-east-1"]).108 """109 raise NotImplementedError110 111 112class EndpointResolver(BaseEndpointResolver):113 """Resolves endpoints based on partition endpoint metadata"""114 115 _UNSUPPORTED_DUALSTACK_PARTITIONS = ['aws-iso', 'aws-iso-b']116 117 def __init__(self, endpoint_data, uses_builtin_data=False):118 """119 :type endpoint_data: dict120 :param endpoint_data: A dict of partition data.121 122 :type uses_builtin_data: boolean123 :param uses_builtin_data: Whether the endpoint data originates in the124 package's data directory.125 """126 if 'partitions' not in endpoint_data:127 raise ValueError('Missing "partitions" in endpoint data')128 self._endpoint_data = endpoint_data129 self.uses_builtin_data = uses_builtin_data130 131 def get_service_endpoints_data(self, service_name, partition_name='aws'):132 for partition in self._endpoint_data['partitions']:133 if partition['partition'] != partition_name:134 continue135 services = partition['services']136 if service_name not in services:137 continue138 return services[service_name]['endpoints']139 140 def get_available_partitions(self):141 result = []142 for partition in self._endpoint_data['partitions']:143 result.append(partition['partition'])144 return result145 146 def get_available_endpoints(147 self,148 service_name,149 partition_name='aws',150 allow_non_regional=False,151 endpoint_variant_tags=None,152 ):153 result = []154 for partition in self._endpoint_data['partitions']:155 if partition['partition'] != partition_name:156 continue157 services = partition['services']158 if service_name not in services:159 continue160 service_endpoints = services[service_name]['endpoints']161 for endpoint_name in service_endpoints:162 is_regional_endpoint = endpoint_name in partition['regions']163 # Only regional endpoints can be modeled with variants164 if endpoint_variant_tags and is_regional_endpoint:165 variant_data = self._retrieve_variant_data(166 service_endpoints[endpoint_name], endpoint_variant_tags167 )168 if variant_data:169 result.append(endpoint_name)170 elif allow_non_regional or is_regional_endpoint:171 result.append(endpoint_name)172 return result173 174 def get_partition_dns_suffix(175 self, partition_name, endpoint_variant_tags=None176 ):177 for partition in self._endpoint_data['partitions']:178 if partition['partition'] == partition_name:179 if endpoint_variant_tags:180 variant = self._retrieve_variant_data(181 partition.get('defaults'), endpoint_variant_tags182 )183 if variant and 'dnsSuffix' in variant:184 return variant['dnsSuffix']185 else:186 return partition['dnsSuffix']187 return None188 189 def construct_endpoint(190 self,191 service_name,192 region_name=None,193 partition_name=None,194 use_dualstack_endpoint=False,195 use_fips_endpoint=False,196 ):197 if (198 service_name == 's3'199 and use_dualstack_endpoint200 and region_name is None201 ):202 region_name = 'us-east-1'203 204 if partition_name is not None:205 valid_partition = None206 for partition in self._endpoint_data['partitions']:207 if partition['partition'] == partition_name:208 valid_partition = partition209 210 if valid_partition is not None:211 result = self._endpoint_for_partition(212 valid_partition,213 service_name,214 region_name,215 use_dualstack_endpoint,216 use_fips_endpoint,217 True,218 )219 return result220 return None221 222 # Iterate over each partition until a match is found.223 for partition in self._endpoint_data['partitions']:224 if use_dualstack_endpoint and (225 partition['partition']226 in self._UNSUPPORTED_DUALSTACK_PARTITIONS227 ):228 continue229 result = self._endpoint_for_partition(230 partition,231 service_name,232 region_name,233 use_dualstack_endpoint,234 use_fips_endpoint,235 )236 if result:237 return result238 239 def get_partition_for_region(self, region_name):240 for partition in self._endpoint_data['partitions']:241 if self._region_match(partition, region_name):242 return partition['partition']243 raise UnknownRegionError(244 region_name=region_name,245 error_msg='No partition found for provided region_name.',246 )247 248 def _endpoint_for_partition(249 self,250 partition,251 service_name,252 region_name,253 use_dualstack_endpoint,254 use_fips_endpoint,255 force_partition=False,256 ):257 partition_name = partition["partition"]258 if (259 use_dualstack_endpoint260 and partition_name in self._UNSUPPORTED_DUALSTACK_PARTITIONS261 ):262 error_msg = (263 "Dualstack endpoints are currently not supported"264 " for %s partition" % partition_name265 )266 raise EndpointVariantError(tags=['dualstack'], error_msg=error_msg)267 268 # Get the service from the partition, or an empty template.269 service_data = partition['services'].get(270 service_name, DEFAULT_SERVICE_DATA271 )272 # Use the partition endpoint if no region is supplied.273 if region_name is None:274 if 'partitionEndpoint' in service_data:275 region_name = service_data['partitionEndpoint']276 else:277 raise NoRegionError()278 279 resolve_kwargs = {280 'partition': partition,281 'service_name': service_name,282 'service_data': service_data,283 'endpoint_name': region_name,284 'use_dualstack_endpoint': use_dualstack_endpoint,285 'use_fips_endpoint': use_fips_endpoint,286 }287 288 # Attempt to resolve the exact region for this partition.289 if region_name in service_data['endpoints']:290 return self._resolve(**resolve_kwargs)291 292 # Check to see if the endpoint provided is valid for the partition.293 if self._region_match(partition, region_name) or force_partition:294 # Use the partition endpoint if set and not regionalized.295 partition_endpoint = service_data.get('partitionEndpoint')296 is_regionalized = service_data.get('isRegionalized', True)297 if partition_endpoint and not is_regionalized:298 LOG.debug(299 'Using partition endpoint for %s, %s: %s',300 service_name,301 region_name,302 partition_endpoint,303 )304 resolve_kwargs['endpoint_name'] = partition_endpoint305 return self._resolve(**resolve_kwargs)306 LOG.debug(307 'Creating a regex based endpoint for %s, %s',308 service_name,309 region_name,310 )311 return self._resolve(**resolve_kwargs)312 313 def _region_match(self, partition, region_name):314 if region_name in partition['regions']:315 return True316 if 'regionRegex' in partition:317 return re.compile(partition['regionRegex']).match(region_name)318 return False319 320 def _retrieve_variant_data(self, endpoint_data, tags):321 variants = endpoint_data.get('variants', [])322 for variant in variants:323 if set(variant['tags']) == set(tags):324 result = variant.copy()325 return result326 327 def _create_tag_list(self, use_dualstack_endpoint, use_fips_endpoint):328 tags = []329 if use_dualstack_endpoint:330 tags.append('dualstack')331 if use_fips_endpoint:332 tags.append('fips')333 return tags334 335 def _resolve_variant(336 self, tags, endpoint_data, service_defaults, partition_defaults337 ):338 result = {}339 for variants in [endpoint_data, service_defaults, partition_defaults]:340 variant = self._retrieve_variant_data(variants, tags)341 if variant:342 self._merge_keys(variant, result)343 return result344 345 def _resolve(346 self,347 partition,348 service_name,349 service_data,350 endpoint_name,351 use_dualstack_endpoint,352 use_fips_endpoint,353 ):354 endpoint_data = service_data.get('endpoints', {}).get(355 endpoint_name, {}356 )357 358 if endpoint_data.get('deprecated'):359 LOG.warning(360 'Client is configured with the deprecated endpoint: %s'361 % (endpoint_name)362 )363 364 service_defaults = service_data.get('defaults', {})365 partition_defaults = partition.get('defaults', {})366 tags = self._create_tag_list(use_dualstack_endpoint, use_fips_endpoint)367 368 if tags:369 result = self._resolve_variant(370 tags, endpoint_data, service_defaults, partition_defaults371 )372 if result == {}:373 error_msg = (374 f"Endpoint does not exist for {service_name} "375 f"in region {endpoint_name}"376 )377 raise EndpointVariantError(tags=tags, error_msg=error_msg)378 self._merge_keys(endpoint_data, result)379 else:380 result = endpoint_data381 382 # If dnsSuffix has not already been consumed from a variant definition383 if 'dnsSuffix' not in result:384 result['dnsSuffix'] = partition['dnsSuffix']385 386 result['partition'] = partition['partition']387 result['endpointName'] = endpoint_name388 389 # Merge in the service defaults then the partition defaults.390 self._merge_keys(service_defaults, result)391 self._merge_keys(partition_defaults, result)392 393 result['hostname'] = self._expand_template(394 partition,395 result['hostname'],396 service_name,397 endpoint_name,398 result['dnsSuffix'],399 )400 if 'sslCommonName' in result:401 result['sslCommonName'] = self._expand_template(402 partition,403 result['sslCommonName'],404 service_name,405 endpoint_name,406 result['dnsSuffix'],407 )408 409 return result410 411 def _merge_keys(self, from_data, result):412 for key in from_data:413 if key not in result:414 result[key] = from_data[key]415 416 def _expand_template(417 self, partition, template, service_name, endpoint_name, dnsSuffix418 ):419 return template.format(420 service=service_name, region=endpoint_name, dnsSuffix=dnsSuffix421 )422 423 424class EndpointResolverBuiltins(str, Enum):425 # The AWS Region configured for the SDK client (str)426 AWS_REGION = "AWS::Region"427 # Whether the UseFIPSEndpoint configuration option has been enabled for428 # the SDK client (bool)429 AWS_USE_FIPS = "AWS::UseFIPS"430 # Whether the UseDualStackEndpoint configuration option has been enabled431 # for the SDK client (bool)432 AWS_USE_DUALSTACK = "AWS::UseDualStack"433 # Whether the global endpoint should be used with STS, rather the the434 # regional endpoint for us-east-1 (bool)435 AWS_STS_USE_GLOBAL_ENDPOINT = "AWS::STS::UseGlobalEndpoint"436 # Whether the global endpoint should be used with S3, rather then the437 # regional endpoint for us-east-1 (bool)438 AWS_S3_USE_GLOBAL_ENDPOINT = "AWS::S3::UseGlobalEndpoint"439 # Whether S3 Transfer Acceleration has been requested (bool)440 AWS_S3_ACCELERATE = "AWS::S3::Accelerate"441 # Whether S3 Force Path Style has been enabled (bool)442 AWS_S3_FORCE_PATH_STYLE = "AWS::S3::ForcePathStyle"443 # Whether to use the ARN region or raise an error when ARN and client444 # region differ (for s3 service only, bool)445 AWS_S3_USE_ARN_REGION = "AWS::S3::UseArnRegion"446 # Whether to use the ARN region or raise an error when ARN and client447 # region differ (for s3-control service only, bool)448 AWS_S3CONTROL_USE_ARN_REGION = 'AWS::S3Control::UseArnRegion'449 # Whether multi-region access points (MRAP) should be disabled (bool)450 AWS_S3_DISABLE_MRAP = "AWS::S3::DisableMultiRegionAccessPoints"451 # Whether a custom endpoint has been configured (str)452 SDK_ENDPOINT = "SDK::Endpoint"453 454 455class EndpointRulesetResolver:456 """Resolves endpoints using a service's endpoint ruleset"""457 458 def __init__(459 self,460 endpoint_ruleset_data,461 partition_data,462 service_model,463 builtins,464 client_context,465 event_emitter,466 use_ssl=True,467 requested_auth_scheme=None,468 ):469 self._provider = EndpointProvider(470 ruleset_data=endpoint_ruleset_data,471 partition_data=partition_data,472 )473 self._param_definitions = self._provider.ruleset.parameters474 self._service_model = service_model475 self._builtins = builtins476 self._client_context = client_context477 self._event_emitter = event_emitter478 self._use_ssl = use_ssl479 self._requested_auth_scheme = requested_auth_scheme480 self._instance_cache = {}481 482 def construct_endpoint(483 self,484 operation_model,485 call_args,486 request_context,487 ):488 """Invokes the provider with params defined in the service's ruleset"""489 if call_args is None:490 call_args = {}491 492 if request_context is None:493 request_context = {}494 495 provider_params = self._get_provider_params(496 operation_model, call_args, request_context497 )498 LOG.debug(499 'Calling endpoint provider with parameters: %s' % provider_params500 )501 try:502 provider_result = self._provider.resolve_endpoint(503 **provider_params504 )505 except EndpointProviderError as ex:506 botocore_exception = self.ruleset_error_to_botocore_exception(507 ex, provider_params508 )509 if botocore_exception is None:510 raise511 else:512 raise botocore_exception from ex513 LOG.debug('Endpoint provider result: %s' % provider_result.url)514 515 # The endpoint provider does not support non-secure transport.516 if not self._use_ssl and provider_result.url.startswith('https://'):517 provider_result = provider_result._replace(518 url=f'http://{provider_result.url[8:]}'519 )520 521 # Multi-valued headers are not supported in botocore. Replace the list522 # of values returned for each header with just its first entry,523 # dropping any additionally entries.524 provider_result = provider_result._replace(525 headers={526 key: val[0] for key, val in provider_result.headers.items()527 }528 )529 530 return provider_result531 532 def _get_provider_params(533 self, operation_model, call_args, request_context534 ):535 """Resolve a value for each parameter defined in the service's ruleset536 537 The resolution order for parameter values is:538 1. Operation-specific static context values from the service definition539 2. Operation-specific dynamic context values from API parameters540 3. Client-specific context parameters541 4. Built-in values such as region, FIPS usage, ...542 """543 provider_params = {}544 # Builtin values can be customized for each operation by hooks545 # subscribing to the ``before-endpoint-resolution.*`` event.546 customized_builtins = self._get_customized_builtins(547 operation_model, call_args, request_context548 )549 for param_name, param_def in self._param_definitions.items():550 param_val = self._resolve_param_from_context(551 param_name=param_name,552 operation_model=operation_model,553 call_args=call_args,554 )555 if param_val is None and param_def.builtin is not None:556 param_val = self._resolve_param_as_builtin(557 builtin_name=param_def.builtin,558 builtins=customized_builtins,559 )560 if param_val is not None:561 provider_params[param_name] = param_val562 563 return provider_params564 565 def _resolve_param_from_context(566 self, param_name, operation_model, call_args567 ):568 static = self._resolve_param_as_static_context_param(569 param_name, operation_model570 )571 if static is not None:572 return static573 dynamic = self._resolve_param_as_dynamic_context_param(574 param_name, operation_model, call_args575 )576 if dynamic is not None:577 return dynamic578 return self._resolve_param_as_client_context_param(param_name)579 580 def _resolve_param_as_static_context_param(581 self, param_name, operation_model582 ):583 static_ctx_params = self._get_static_context_params(operation_model)584 return static_ctx_params.get(param_name)585 586 def _resolve_param_as_dynamic_context_param(587 self, param_name, operation_model, call_args588 ):589 dynamic_ctx_params = self._get_dynamic_context_params(operation_model)590 if param_name in dynamic_ctx_params:591 member_name = dynamic_ctx_params[param_name]592 return call_args.get(member_name)593 594 def _resolve_param_as_client_context_param(self, param_name):595 client_ctx_params = self._get_client_context_params()596 if param_name in client_ctx_params:597 client_ctx_varname = client_ctx_params[param_name]598 return self._client_context.get(client_ctx_varname)599 600 def _resolve_param_as_builtin(self, builtin_name, builtins):601 if builtin_name not in EndpointResolverBuiltins.__members__.values():602 raise UnknownEndpointResolutionBuiltInName(name=builtin_name)603 return builtins.get(builtin_name)604 605 @instance_cache606 def _get_static_context_params(self, operation_model):607 """Mapping of param names to static param value for an operation"""608 return {609 param.name: param.value610 for param in operation_model.static_context_parameters611 }612 613 @instance_cache614 def _get_dynamic_context_params(self, operation_model):615 """Mapping of param names to member names for an operation"""616 return {617 param.name: param.member_name618 for param in operation_model.context_parameters619 }620 621 @instance_cache622 def _get_client_context_params(self):623 """Mapping of param names to client configuration variable"""624 return {625 param.name: xform_name(param.name)626 for param in self._service_model.client_context_parameters627 }628 629 def _get_customized_builtins(630 self, operation_model, call_args, request_context631 ):632 service_id = self._service_model.service_id.hyphenize()633 customized_builtins = copy.copy(self._builtins)634 # Handlers are expected to modify the builtins dict in place.635 self._event_emitter.emit(636 'before-endpoint-resolution.%s' % service_id,637 builtins=customized_builtins,638 model=operation_model,639 params=call_args,640 context=request_context,641 )642 return customized_builtins643 644 def auth_schemes_to_signing_ctx(self, auth_schemes):645 """Convert an Endpoint's authSchemes property to a signing_context dict646 647 :type auth_schemes: list648 :param auth_schemes: A list of dictionaries taken from the649 ``authSchemes`` property of an Endpoint object returned by650 ``EndpointProvider``.651 652 :rtype: str, dict653 :return: Tuple of auth type string (to be used in654 ``request_context['auth_type']``) and signing context dict (for use655 in ``request_context['signing']``).656 """657 if not isinstance(auth_schemes, list) or len(auth_schemes) == 0:658 raise TypeError("auth_schemes must be a non-empty list.")659 660 LOG.debug(661 'Selecting from endpoint provider\'s list of auth schemes: %s. '662 'User selected auth scheme is: "%s"',663 ', '.join([f'"{s.get("name")}"' for s in auth_schemes]),664 self._requested_auth_scheme,665 )666 667 if self._requested_auth_scheme == UNSIGNED:668 return 'none', {}669 670 auth_schemes = [671 {**scheme, 'name': self._strip_sig_prefix(scheme['name'])}672 for scheme in auth_schemes673 ]674 if self._requested_auth_scheme is not None:675 try:676 # Use the first scheme that matches the requested scheme,677 # after accounting for naming differences between botocore and678 # endpoint rulesets. Keep the requested name.679 name, scheme = next(680 (self._requested_auth_scheme, s)681 for s in auth_schemes682 if self._does_botocore_authname_match_ruleset_authname(683 self._requested_auth_scheme, s['name']684 )685 )686 except StopIteration:687 # For legacy signers, no match will be found. Do not raise an688 # exception, instead default to the logic in botocore689 # customizations.690 return None, {}691 else:692 try:693 name, scheme = next(694 (s['name'], s)695 for s in auth_schemes696 if s['name'] in AUTH_TYPE_MAPS697 )698 except StopIteration:699 # If no auth scheme was specifically requested and an700 # authSchemes list is present in the Endpoint object but none701 # of the entries are supported, raise an exception.702 fixable_with_crt = False703 auth_type_options = [s['name'] for s in auth_schemes]704 if not HAS_CRT:705 fixable_with_crt = any(706 scheme in CRT_SUPPORTED_AUTH_TYPES707 for scheme in auth_type_options708 )709 710 if fixable_with_crt:711 raise MissingDependencyException(712 msg='This operation requires an additional dependency.'713 ' Use pip install botocore[crt] before proceeding.'714 )715 else:716 raise UnknownSignatureVersionError(717 signature_version=', '.join(auth_type_options)718 )719 720 signing_context = {}721 if 'signingRegion' in scheme:722 signing_context['region'] = scheme['signingRegion']723 elif 'signingRegionSet' in scheme:724 if len(scheme['signingRegionSet']) > 0:725 signing_context['region'] = scheme['signingRegionSet'][0]726 if 'signingName' in scheme:727 signing_context.update(signing_name=scheme['signingName'])728 if 'disableDoubleEncoding' in scheme:729 signing_context['disableDoubleEncoding'] = ensure_boolean(730 scheme['disableDoubleEncoding']731 )732 733 LOG.debug(734 'Selected auth type "%s" as "%s" with signing context params: %s',735 scheme['name'], # original name without "sig"736 name, # chosen name can differ when `signature_version` is set737 signing_context,738 )739 return name, signing_context740 741 def _strip_sig_prefix(self, auth_name):742 """Normalize auth type names by removing any "sig" prefix"""743 return auth_name[3:] if auth_name.startswith('sig') else auth_name744 745 def _does_botocore_authname_match_ruleset_authname(self, botoname, rsname):746 """747 Whether a valid string provided as signature_version parameter for748 client construction refers to the same auth methods as a string749 returned by the endpoint ruleset provider. This accounts for:750 751 * The ruleset prefixes auth names with "sig"752 * The s3 and s3control rulesets don't distinguish between v4[a] and753 s3v4[a] signers754 * The v2, v3, and HMAC v1 based signers (s3, s3-*) are botocore legacy755 features and do not exist in the rulesets756 * Only characters up to the first dash are considered757 758 Example matches:759 * v4, sigv4760 * v4, v4761 * s3v4, sigv4762 * s3v7, sigv7 (hypothetical example)763 * s3v4a, sigv4a764 * s3v4-query, sigv4765 766 Example mismatches:767 * v4a, sigv4768 * s3, sigv4769 * s3-presign-post, sigv4770 """771 rsname = self._strip_sig_prefix(rsname)772 botoname = botoname.split('-')[0]773 if botoname != 's3' and botoname.startswith('s3'):774 botoname = botoname[2:]775 return rsname == botoname776 777 def ruleset_error_to_botocore_exception(self, ruleset_exception, params):778 """Attempts to translate ruleset errors to pre-existing botocore779 exception types by string matching exception strings.780 """781 msg = ruleset_exception.kwargs.get('msg')782 if msg is None:783 return784 785 if msg.startswith('Invalid region in ARN: '):786 # Example message:787 # "Invalid region in ARN: `us-we$t-2` (invalid DNS name)"788 try:789 label = msg.split('`')[1]790 except IndexError:791 label = msg792 return InvalidHostLabelError(label=label)793 794 service_name = self._service_model.service_name795 if service_name == 's3':796 if (797 msg == 'S3 Object Lambda does not support S3 Accelerate'798 or msg == 'Accelerate cannot be used with FIPS'799 ):800 return UnsupportedS3ConfigurationError(msg=msg)801 if (802 msg.startswith('S3 Outposts does not support')803 or msg.startswith('S3 MRAP does not support')804 or msg.startswith('S3 Object Lambda does not support')805 or msg.startswith('Access Points do not support')806 or msg.startswith('Invalid configuration:')807 or msg.startswith('Client was configured for partition')808 ):809 return UnsupportedS3AccesspointConfigurationError(msg=msg)810 if msg.lower().startswith('invalid arn:'):811 return ParamValidationError(report=msg)812 if service_name == 's3control':813 if msg.startswith('Invalid ARN:'):814 arn = params.get('Bucket')815 return UnsupportedS3ControlArnError(arn=arn, msg=msg)816 if msg.startswith('Invalid configuration:') or msg.startswith(817 'Client was configured for partition'818 ):819 return UnsupportedS3ControlConfigurationError(msg=msg)820 if msg == "AccountId is required but not set":821 return ParamValidationError(report=msg)822 if service_name == 'events':823 if msg.startswith(824 'Invalid Configuration: FIPS is not supported with '825 'EventBridge multi-region endpoints.'826 ):827 return InvalidEndpointConfigurationError(msg=msg)828 if msg == 'EndpointId must be a valid host label.':829 return InvalidEndpointConfigurationError(msg=msg)830 return None831 