Repository: boto/botocore
Pull request #808 (2016-02-24 22:18:20)

Fail gracefully when no credentials are found 
Regression on error condition from when the refresh credential bug fix merged.

You'll now get the original error message:

```
Traceback (most recent call last):
  File "/tmp/t.py", line 5, in <module>
    print ec2.describe_instances()
  File "botocore/botocore/client.py", line 301, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "botocore/botocore/client.py", line 387, in _make_api_call
    operation_model, request_dict)
  File "botocore/botocore/endpoint.py", line 117, in make_request
    return self._send_request(request_dict, operation_model)
  File "botocore/botocore/endpoint.py", line 142, in _send_request
    request = self.create_request(request_dict, operation_model)
  File "botocore/botocore/endpoint.py", line 126, in create_request
    operation_name=operation_model.name)
  File "botocore/botocore/hooks.py", line 226, in emit
    return self._emit(event_name, kwargs)
  File "botocore/botocore/hooks.py", line 209, in _emit
    response = handler(**kwargs)
  File "botocore/botocore/signers.py", line 90, in handler
    return self.sign(operation_name, request)
  File "botocore/botocore/signers.py", line 124, in sign
    signer.add_auth(request=request)
  File "botocore/botocore/auth.py", line 309, in add_auth
    raise NoCredentialsError
botocore.exceptions.NoCredentialsError: Unable to locate credentials
```

Fixes boto/boto3#498

cc @kyleknap @rayluo @JordonPhillips 


Patch:
diff --git a/botocore/signers.py b/botocore/signers.py
index 846dfe871..d586b8d3f 100644
--- a/botocore/signers.py
+++ b/botocore/signers.py
@@ -150,7 +150,14 @@ class RequestSigner(object):
         if cls is None:
             raise UnknownSignatureVersionError(
                 signature_version=signature_version)
-        kwargs['credentials'] = self._credentials.get_frozen_credentials()
+        # If there's no credentials provided (i.e credentials is None),
+        # then we'll pass a value of "None" over to the auth classes,
+        # which already handle the cases where no credentials have
+        # been provided.
+        frozen_credentials = None
+        if self._credentials is not None:
+            frozen_credentials = self._credentials.get_frozen_credentials()
+        kwargs['credentials'] = frozen_credentials
         if cls.REQUIRES_REGION:
             if self._region_name is None:
                 raise botocore.exceptions.NoRegionError()


Test patch:
diff --git a/tests/unit/test_signers.py b/tests/unit/test_signers.py
index ec9bde350..3760bad60 100644
--- a/tests/unit/test_signers.py
+++ b/tests/unit/test_signers.py
@@ -254,6 +254,25 @@ class TestSigner(BaseSignerTest):
                 service_name='service_name',
                 region_name='region_name')
 
+    def test_no_credentials_case_is_forwarded_to_signer(self):
+        # If no credentials are given to the RequestSigner, we should
+        # forward that fact on to the Auth class and let them handle
+        # the error (which they already do).
+        self.credentials = None
+        self.signer = RequestSigner(
+            'service_name', 'region_name', 'signing_name',
+            'v4', self.credentials, self.emitter)
+        auth_cls = mock.Mock()
+        with mock.patch.dict(botocore.auth.AUTH_TYPE_MAPS,
+                             {'v4': auth_cls}):
+            auth = self.signer.get_auth_instance(
+                'service_name', 'region_name', 'v4')
+            auth_cls.assert_called_with(
+                service_name='service_name',
+                region_name='region_name',
+                credentials=None,
+            )
+
 
 class TestCloudfrontSigner(unittest.TestCase):
     def setUp(self):
@@ -277,11 +296,11 @@ class TestCloudfrontSigner(unittest.TestCase):
             ip_address='12.34.56.78/9')
         expected = {
             "Statement": [{
-                "Resource":"foo",
-                "Condition":{
-                    "DateGreaterThan":{"AWS:EpochTime":1448928000},
-                    "DateLessThan":{"AWS:EpochTime":1451606400},
-                    "IpAddress":{"AWS:SourceIp":"12.34.56.78/9"}
+                "Resource": "foo",
+                "Condition": {
+                    "DateGreaterThan": {"AWS:EpochTime": 1448928000},
+                    "DateLessThan": {"AWS:EpochTime": 1451606400},
+                    "IpAddress": {"AWS:SourceIp": "12.34.56.78/9"}
                 },
             }]
         }
@@ -320,10 +339,10 @@ class TestCloudfrontSigner(unittest.TestCase):
         expected = (
             'http://test.com/index.html?foo=bar'
             '&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiZm9vIiwiQ29uZ'
-                'Gl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIj'
-                'oxNDUxNjA2NDAwfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI'
-                '6IjEyLjM0LjU2Ljc4LzkifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFX'
-                'UzpFcG9jaFRpbWUiOjE0NDg5MjgwMDB9fX1dfQ__'
+            'Gl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIj'
+            'oxNDUxNjA2NDAwfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI'
+            '6IjEyLjM0LjU2Ljc4LzkifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFX'
+            'UzpFcG9jaFRpbWUiOjE0NDg5MjgwMDB9fX1dfQ__'
             '&Signature=c2lnbmVk&Key-Pair-Id=MY_KEY_ID')
         self.assertEqualUrl(signed_url, expected)