1# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
2# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"). You
5# may not use this file except in compliance with the License. A copy of
6# the License is located at
7#
8# http://aws.amazon.com/apache2.0/
9#
10# or in the "license" file accompanying this file. This file is
11# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12# ANY KIND, either express or implied. See the License for the specific
13# language governing permissions and limitations under the License.
14from __future__ import unicode_literals
15from botocore.vendored import requests
16from botocore.vendored.requests.packages import urllib3
17
18
19def _exception_from_packed_args(exception_cls, args=None, kwargs=None):
20    # This is helpful for reducing Exceptions that only accept kwargs as
21    # only positional arguments can be provided for __reduce__
22    # Ideally, this would also be a class method on the BotoCoreError
23    # but instance methods cannot be pickled.
24    if args is None:
25        args = ()
26    if kwargs is None:
27        kwargs = {}
28    return exception_cls(*args, **kwargs)
29
30
31class BotoCoreError(Exception):
32    """
33    The base exception class for BotoCore exceptions.
34
35    :ivar msg: The descriptive message associated with the error.
36    """
37    fmt = 'An unspecified error occurred'
38
39    def __init__(self, **kwargs):
40        msg = self.fmt.format(**kwargs)
41        Exception.__init__(self, msg)
42        self.kwargs = kwargs
43
44    def __reduce__(self):
45        return _exception_from_packed_args, (self.__class__, None, self.kwargs)
46
47
48class DataNotFoundError(BotoCoreError):
49    """
50    The data associated with a particular path could not be loaded.
51
52    :ivar data_path: The data path that the user attempted to load.
53    """
54    fmt = 'Unable to load data for: {data_path}'
55
56
57class UnknownServiceError(DataNotFoundError):
58    """Raised when trying to load data for an unknown service.
59
60    :ivar service_name: The name of the unknown service.
61
62    """
63    fmt = (
64        "Unknown service: '{service_name}'. Valid service names are: "
65        "{known_service_names}")
66
67
68class ApiVersionNotFoundError(BotoCoreError):
69    """
70    The data associated with either the API version or a compatible one
71    could not be loaded.
72
73    :ivar data_path: The data path that the user attempted to load.
74    :ivar api_version: The API version that the user attempted to load.
75    """
76    fmt = 'Unable to load data {data_path} for: {api_version}'
77
78
79class HTTPClientError(BotoCoreError):
80    fmt = 'An HTTP Client raised an unhandled exception: {error}'
81
82    def __init__(self, request=None, response=None, **kwargs):
83        self.request = request
84        self.response = response
85        super(HTTPClientError, self).__init__(**kwargs)
86
87    def __reduce__(self):
88        return _exception_from_packed_args, (
89            self.__class__, (self.request, self.response), self.kwargs)
90
91
92class ConnectionError(BotoCoreError):
93    fmt = 'An HTTP Client failed to establish a connection: {error}'
94
95
96class InvalidIMDSEndpointError(BotoCoreError):
97    fmt = 'Invalid endpoint EC2 Instance Metadata endpoint: {endpoint}'
98
99
100class InvalidIMDSEndpointModeError(BotoCoreError):
101    fmt = (
102        'Invalid EC2 Instance Metadata endpoint mode: {mode}'
103        ' Valid endpoint modes (case-insensitive): {valid_modes}.'
104    )
105
106
107class EndpointConnectionError(ConnectionError):
108    fmt = 'Could not connect to the endpoint URL: "{endpoint_url}"'
109
110
111class SSLError(ConnectionError, requests.exceptions.SSLError):
112    fmt = 'SSL validation failed for {endpoint_url} {error}'
113
114
115class ConnectionClosedError(HTTPClientError):
116    fmt = (
117        'Connection was closed before we received a valid response '
118        'from endpoint URL: "{endpoint_url}".')
119
120
121class ReadTimeoutError(HTTPClientError, requests.exceptions.ReadTimeout,
122                       urllib3.exceptions.ReadTimeoutError):
123    fmt = 'Read timeout on endpoint URL: "{endpoint_url}"'
124
125
126class ConnectTimeoutError(ConnectionError, requests.exceptions.ConnectTimeout):
127    fmt = 'Connect timeout on endpoint URL: "{endpoint_url}"'
128
129
130class ProxyConnectionError(ConnectionError, requests.exceptions.ProxyError):
131    fmt = 'Failed to connect to proxy URL: "{proxy_url}"'
132
133
134class NoCredentialsError(BotoCoreError):
135    """
136    No credentials could be found.
137    """
138    fmt = 'Unable to locate credentials'
139
140
141class PartialCredentialsError(BotoCoreError):
142    """
143    Only partial credentials were found.
144
145    :ivar cred_var: The missing credential variable name.
146
147    """
148    fmt = 'Partial credentials found in {provider}, missing: {cred_var}'
149
150
151class CredentialRetrievalError(BotoCoreError):
152    """
153    Error attempting to retrieve credentials from a remote source.
154
155    :ivar provider: The name of the credential provider.
156    :ivar error_msg: The msg explaining why credentials could not be
157        retrieved.
158
159    """
160    fmt = 'Error when retrieving credentials from {provider}: {error_msg}'
161
162
163class UnknownSignatureVersionError(BotoCoreError):
164    """
165    Requested Signature Version is not known.
166
167    :ivar signature_version: The name of the requested signature version.
168    """
169    fmt = 'Unknown Signature Version: {signature_version}.'
170
171
172class ServiceNotInRegionError(BotoCoreError):
173    """
174    The service is not available in requested region.
175
176    :ivar service_name: The name of the service.
177    :ivar region_name: The name of the region.
178    """
179    fmt = 'Service {service_name} not available in region {region_name}'
180
181
182class BaseEndpointResolverError(BotoCoreError):
183    """Base error for endpoint resolving errors.
184
185    Should never be raised directly, but clients can catch
186    this exception if they want to generically handle any errors
187    during the endpoint resolution process.
188
189    """
190
191
192class NoRegionError(BaseEndpointResolverError):
193    """No region was specified."""
194    fmt = 'You must specify a region.'
195
196
197class UnknownEndpointError(BaseEndpointResolverError, ValueError):
198    """
199    Could not construct an endpoint.
200
201    :ivar service_name: The name of the service.
202    :ivar region_name: The name of the region.
203    """
204    fmt = (
205        'Unable to construct an endpoint for '
206        '{service_name} in region {region_name}')
207
208
209class UnknownFIPSEndpointError(BaseEndpointResolverError):
210    """
211    Could not construct a FIPS endpoint.
212
213    :ivar service_name: The name of the service.
214    :ivar region_name: The name of the region.
215    """
216    fmt = (
217        'The provided FIPS pseudo-region "{region_name}" is not known for '
218        'the service "{service_name}". A FIPS compliant endpoint cannot be '
219        'constructed.'
220    )
221
222
223class ProfileNotFound(BotoCoreError):
224    """
225    The specified configuration profile was not found in the
226    configuration file.
227
228    :ivar profile: The name of the profile the user attempted to load.
229    """
230    fmt = 'The config profile ({profile}) could not be found'
231
232
233class ConfigParseError(BotoCoreError):
234    """
235    The configuration file could not be parsed.
236
237    :ivar path: The path to the configuration file.
238    """
239    fmt = 'Unable to parse config file: {path}'
240
241
242class ConfigNotFound(BotoCoreError):
243    """
244    The specified configuration file could not be found.
245
246    :ivar path: The path to the configuration file.
247    """
248    fmt = 'The specified config file ({path}) could not be found.'
249
250
251class MissingParametersError(BotoCoreError):
252    """
253    One or more required parameters were not supplied.
254
255    :ivar object: The object that has missing parameters.
256        This can be an operation or a parameter (in the
257        case of inner params).  The str() of this object
258        will be used so it doesn't need to implement anything
259        other than str().
260    :ivar missing: The names of the missing parameters.
261    """
262    fmt = ('The following required parameters are missing for '
263           '{object_name}: {missing}')
264
265
266class ValidationError(BotoCoreError):
267    """
268    An exception occurred validating parameters.
269
270    Subclasses must accept a ``value`` and ``param``
271    argument in their ``__init__``.
272
273    :ivar value: The value that was being validated.
274    :ivar param: The parameter that failed validation.
275    :ivar type_name: The name of the underlying type.
276    """
277    fmt = ("Invalid value ('{value}') for param {param} "
278           "of type {type_name} ")
279
280
281class ParamValidationError(BotoCoreError):
282    fmt = 'Parameter validation failed:\n{report}'
283
284
285# These exceptions subclass from ValidationError so that code
286# can just 'except ValidationError' to catch any possibly validation
287# error.
288class UnknownKeyError(ValidationError):
289    """
290    Unknown key in a struct parameter.
291
292    :ivar value: The value that was being checked.
293    :ivar param: The name of the parameter.
294    :ivar choices: The valid choices the value can be.
295    """
296    fmt = ("Unknown key '{value}' for param '{param}'.  Must be one "
297           "of: {choices}")
298
299
300class RangeError(ValidationError):
301    """
302    A parameter value was out of the valid range.
303
304    :ivar value: The value that was being checked.
305    :ivar param: The parameter that failed validation.
306    :ivar min_value: The specified minimum value.
307    :ivar max_value: The specified maximum value.
308    """
309    fmt = ('Value out of range for param {param}: '
310           '{min_value} <= {value} <= {max_value}')
311
312
313class UnknownParameterError(ValidationError):
314    """
315    Unknown top level parameter.
316
317    :ivar name: The name of the unknown parameter.
318    :ivar operation: The name of the operation.
319    :ivar choices: The valid choices the parameter name can be.
320    """
321    fmt = (
322        "Unknown parameter '{name}' for operation {operation}.  Must be one "
323        "of: {choices}"
324    )
325
326
327class InvalidRegionError(ValidationError, ValueError):
328    """
329    Invalid region_name provided to client or resource.
330
331    :ivar region_name: region_name that was being validated.
332    """
333    fmt = (
334        "Provided region_name '{region_name}' doesn't match a supported format."
335    )
336
337
338class AliasConflictParameterError(ValidationError):
339    """
340    Error when an alias is provided for a parameter as well as the original.
341
342    :ivar original: The name of the original parameter.
343    :ivar alias: The name of the alias
344    :ivar operation: The name of the operation.
345    """
346    fmt = (
347        "Parameter '{original}' and its alias '{alias}' were provided "
348        "for operation {operation}.  Only one of them may be used."
349    )
350
351
352class UnknownServiceStyle(BotoCoreError):
353    """
354    Unknown style of service invocation.
355
356    :ivar service_style: The style requested.
357    """
358    fmt = 'The service style ({service_style}) is not understood.'
359
360
361class PaginationError(BotoCoreError):
362    fmt = 'Error during pagination: {message}'
363
364
365class OperationNotPageableError(BotoCoreError):
366    fmt = 'Operation cannot be paginated: {operation_name}'
367
368
369class ChecksumError(BotoCoreError):
370    """The expected checksum did not match the calculated checksum.
371
372    """
373    fmt = ('Checksum {checksum_type} failed, expected checksum '
374           '{expected_checksum} did not match calculated checksum '
375           '{actual_checksum}.')
376
377
378class UnseekableStreamError(BotoCoreError):
379    """Need to seek a stream, but stream does not support seeking.
380
381    """
382    fmt = ('Need to rewind the stream {stream_object}, but stream '
383           'is not seekable.')
384
385
386class WaiterError(BotoCoreError):
387    """Waiter failed to reach desired state."""
388    fmt = 'Waiter {name} failed: {reason}'
389
390    def __init__(self, name, reason, last_response):
391        super(WaiterError, self).__init__(name=name, reason=reason)
392        self.last_response = last_response
393
394
395class IncompleteReadError(BotoCoreError):
396    """HTTP response did not return expected number of bytes."""
397    fmt = ('{actual_bytes} read, but total bytes '
398           'expected is {expected_bytes}.')
399
400
401class InvalidExpressionError(BotoCoreError):
402    """Expression is either invalid or too complex."""
403    fmt = 'Invalid expression {expression}: Only dotted lookups are supported.'
404
405
406class UnknownCredentialError(BotoCoreError):
407    """Tried to insert before/after an unregistered credential type."""
408    fmt = 'Credential named {name} not found.'
409
410
411class WaiterConfigError(BotoCoreError):
412    """Error when processing waiter configuration."""
413    fmt = 'Error processing waiter config: {error_msg}'
414
415
416class UnknownClientMethodError(BotoCoreError):
417    """Error when trying to access a method on a client that does not exist."""
418    fmt = 'Client does not have method: {method_name}'
419
420
421class UnsupportedSignatureVersionError(BotoCoreError):
422    """Error when trying to use an unsupported Signature Version."""
423    fmt = 'Signature version is not supported: {signature_version}'
424
425
426class ClientError(Exception):
427    MSG_TEMPLATE = (
428        'An error occurred ({error_code}) when calling the {operation_name} '
429        'operation{retry_info}: {error_message}')
430
431    def __init__(self, error_response, operation_name):
432        retry_info = self._get_retry_info(error_response)
433        error = error_response.get('Error', {})
434        msg = self.MSG_TEMPLATE.format(
435            error_code=error.get('Code', 'Unknown'),
436            error_message=error.get('Message', 'Unknown'),
437            operation_name=operation_name,
438            retry_info=retry_info,
439        )
440        super(ClientError, self).__init__(msg)
441        self.response = error_response
442        self.operation_name = operation_name
443
444    def _get_retry_info(self, response):
445        retry_info = ''
446        if 'ResponseMetadata' in response:
447            metadata = response['ResponseMetadata']
448            if metadata.get('MaxAttemptsReached', False):
449                if 'RetryAttempts' in metadata:
450                    retry_info = (' (reached max retries: %s)' %
451                                  metadata['RetryAttempts'])
452        return retry_info
453
454    def __reduce__(self):
455        # Subclasses of ClientError's are dynamically generated and
456        # cannot be pickled unless they are attributes of a
457        # module. So at the very least return a ClientError back.
458        return ClientError, (self.response, self.operation_name)
459
460
461class EventStreamError(ClientError):
462    pass
463
464
465class UnsupportedTLSVersionWarning(Warning):
466    """Warn when an openssl version that uses TLS 1.2 is required"""
467    pass
468
469
470class ImminentRemovalWarning(Warning):
471    pass
472
473
474class InvalidDNSNameError(BotoCoreError):
475    """Error when virtual host path is forced on a non-DNS compatible bucket"""
476    fmt = (
477        'Bucket named {bucket_name} is not DNS compatible. Virtual '
478        'hosted-style addressing cannot be used. The addressing style '
479        'can be configured by removing the addressing_style value '
480        'or setting that value to \'path\' or \'auto\' in the AWS Config '
481        'file or in the botocore.client.Config object.'
482    )
483
484
485class InvalidS3AddressingStyleError(BotoCoreError):
486    """Error when an invalid path style is specified"""
487    fmt = (
488        'S3 addressing style {s3_addressing_style} is invalid. Valid options '
489        'are: \'auto\', \'virtual\', and \'path\''
490    )
491
492
493class UnsupportedS3ArnError(BotoCoreError):
494    """Error when S3 ARN provided to Bucket parameter is not supported"""
495    fmt = (
496        'S3 ARN {arn} provided to "Bucket" parameter is invalid. Only '
497        'ARNs for S3 access-points are supported.'
498    )
499
500
501class UnsupportedS3ControlArnError(BotoCoreError):
502    """Error when S3 ARN provided to S3 control parameter is not supported"""
503    fmt = (
504        'S3 ARN "{arn}" provided is invalid for this operation. {msg}'
505    )
506
507
508class InvalidHostLabelError(BotoCoreError):
509    """Error when an invalid host label would be bound to an endpoint"""
510    fmt = (
511        'Invalid host label to be bound to the hostname of the endpoint: '
512        '"{label}".'
513    )
514
515
516class UnsupportedOutpostResourceError(BotoCoreError):
517    """Error when S3 Outpost ARN provided to Bucket parameter is incomplete"""
518    fmt = (
519        'S3 Outpost ARN resource "{resource_name}" provided to "Bucket" '
520        'parameter is invalid. Only ARNs for S3 Outpost arns with an '
521        'access-point sub-resource are supported.'
522    )
523
524
525class UnsupportedS3ConfigurationError(BotoCoreError):
526    """Error when an unsupported configuration is used with access-points"""
527    fmt = (
528        'Unsupported configuration when using S3: {msg}'
529    )
530
531
532class UnsupportedS3AccesspointConfigurationError(BotoCoreError):
533    """Error when an unsupported configuration is used with access-points"""
534    fmt = (
535        'Unsupported configuration when using S3 access-points: {msg}'
536    )
537
538
539class InvalidEndpointDiscoveryConfigurationError(BotoCoreError):
540    """Error when invalid value supplied for endpoint_discovery_enabled"""
541    fmt = (
542        'Unsupported configuration value for endpoint_discovery_enabled. '
543        'Expected one of ("true", "false", "auto") but got {config_value}.'
544    )
545
546
547class UnsupportedS3ControlConfigurationError(BotoCoreError):
548    """Error when an unsupported configuration is used with S3 Control"""
549    fmt = (
550        'Unsupported configuration when using S3 Control: {msg}'
551    )
552
553
554class InvalidRetryConfigurationError(BotoCoreError):
555    """Error when invalid retry configuration is specified"""
556    fmt = (
557        'Cannot provide retry configuration for "{retry_config_option}". '
558        'Valid retry configuration options are: \'max_attempts\''
559    )
560
561
562class InvalidMaxRetryAttemptsError(InvalidRetryConfigurationError):
563    """Error when invalid retry configuration is specified"""
564    fmt = (
565        'Value provided to "max_attempts": {provided_max_attempts} must '
566        'be an integer greater than or equal to {min_value}.'
567    )
568
569
570class InvalidRetryModeError(InvalidRetryConfigurationError):
571    """Error when invalid retry mode configuration is specified"""
572    fmt = (
573        'Invalid value provided to "mode": "{provided_retry_mode}" must '
574        'be one of: "legacy", "standard", "adaptive"'
575    )
576
577
578class InvalidS3UsEast1RegionalEndpointConfigError(BotoCoreError):
579    """Error for invalid s3 us-east-1 regional endpoints configuration"""
580    fmt = (
581        'S3 us-east-1 regional endpoint option '
582        '{s3_us_east_1_regional_endpoint_config} is '
583        'invalid. Valid options are: "legacy", "regional"'
584    )
585
586
587class InvalidSTSRegionalEndpointsConfigError(BotoCoreError):
588    """Error when invalid sts regional endpoints configuration is specified"""
589    fmt = (
590        'STS regional endpoints option {sts_regional_endpoints_config} is '
591        'invalid. Valid options are: "legacy", "regional"'
592    )
593
594
595class StubResponseError(BotoCoreError):
596    fmt = 'Error getting response stub for operation {operation_name}: {reason}'
597
598
599class StubAssertionError(StubResponseError, AssertionError):
600    pass
601
602
603class UnStubbedResponseError(StubResponseError):
604    pass
605
606
607class InvalidConfigError(BotoCoreError):
608    fmt = '{error_msg}'
609
610
611class InfiniteLoopConfigError(InvalidConfigError):
612    fmt = (
613        'Infinite loop in credential configuration detected. Attempting to '
614        'load from profile {source_profile} which has already been visited. '
615        'Visited profiles: {visited_profiles}'
616    )
617
618
619class RefreshWithMFAUnsupportedError(BotoCoreError):
620    fmt = 'Cannot refresh credentials: MFA token required.'
621
622
623class MD5UnavailableError(BotoCoreError):
624    fmt = "This system does not support MD5 generation."
625
626
627class MissingDependencyException(BotoCoreError):
628    fmt = "Missing Dependency: {msg}"
629
630
631class MetadataRetrievalError(BotoCoreError):
632    fmt = "Error retrieving metadata: {error_msg}"
633
634
635class UndefinedModelAttributeError(Exception):
636    pass
637
638
639class MissingServiceIdError(UndefinedModelAttributeError):
640    fmt = (
641        "The model being used for the service {service_name} is missing the "
642        "serviceId metadata property, which is required."
643    )
644
645    def __init__(self, **kwargs):
646        msg = self.fmt.format(**kwargs)
647        Exception.__init__(self, msg)
648        self.kwargs = kwargs
649
650
651class SSOError(BotoCoreError):
652    fmt = "An unspecified error happened when resolving SSO credentials"
653
654
655class SSOTokenLoadError(SSOError):
656    fmt = "Error loading SSO Token: {error_msg}"
657
658
659class UnauthorizedSSOTokenError(SSOError):
660    fmt = (
661        "The SSO session associated with this profile has expired or is "
662        "otherwise invalid. To refresh this SSO session run aws sso login "
663        "with the corresponding profile."
664    )
665
666
667class CapacityNotAvailableError(BotoCoreError):
668    fmt = (
669        'Insufficient request capacity available.'
670    )
671
672
673class InvalidProxiesConfigError(BotoCoreError):
674    fmt = (
675        'Invalid configuration value(s) provided for proxies_config.'
676    )
677