Skip to content

Commit

Permalink
Improve error message for sigv4 and PermanentRedirect errors
Browse files Browse the repository at this point in the history
The error message now includes a note on specifying the
correct region:

 $ aws s3api list-objects --bucket wrong.region.bucket

 A client error (PermanentRedirect) occurred when calling the ListObjects operation:

 The bucket you are attempting to access must be addressed using the specified
 endpoint. Please send all future requests to this endpoint:
 wrong.region.bucket.s3-ap-southeast-2.amazonaws.com
 You can fix this issue by explicity providing the correct region location
 using the --region argument, the AWS_DEFAULT_REGION environment variable, or
 the region variable in the AWS CLI configuration file.  You can get the
 bucket's location by running "aws s3api get-bucket-location --bucket BUCKET".

Same thing when you try to access a bucket in eu-central-1.
  • Loading branch information
jamesls committed Oct 24, 2014
1 parent 974968a commit 44f3b0f
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 1 deletion.
56 changes: 56 additions & 0 deletions awscli/customizations/s3errormsg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""Give better S3 error messages.
"""
from awscli.customizations import utils


REGION_ERROR_MSG = (
'You can fix this issue by explicity providing the correct region '
'location using the --region argument, the AWS_DEFAULT_REGION '
'environment variable, or the region variable in the AWS CLI '
"configuration file. You can get the bucket's location by "
'running "aws s3api get-bucket-location --bucket BUCKET".'
)


def register_s3_error_msg(event_handlers):
event_handlers.register('after-call.s3', enhance_error_msg)


def enhance_error_msg(parsed, **kwargs):
if parsed is None or 'Error' not in parsed:
# There's no error message to enhance so we can continue.
return
if _is_sigv4_error_message(parsed):
message = (
'You are attempting to operate on a bucket in a region '
'that requires Signature Version 4. '
)
message += REGION_ERROR_MSG
parsed['Error']['Message'] = message
elif _is_permanent_redirect_message(parsed):
endpoint = parsed['Error']['Endpoint']
message = parsed['Error']['Message']
new_message = message[:-1] + ': %s\n' % endpoint
new_message += REGION_ERROR_MSG
parsed['Error']['Message'] = new_message


def _is_sigv4_error_message(parsed):
return ('Please use AWS4-HMAC-SHA256' in
parsed.get('Error', {}).get('Message', ''))


def _is_permanent_redirect_message(parsed):
return parsed.get('Error', {}).get('Code', '') == 'PermanentRedirect'
6 changes: 5 additions & 1 deletion awscli/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,18 @@
from awscli.customizations.emr.emr import emr_initialize
from awscli.customizations.cloudsearchdomain import register_cloudsearchdomain
from awscli.customizations.s3endpoint import register_s3_endpoint
from awscli.customizations.s3errormsg import register_s3_error_msg


def awscli_initialize(event_handlers):
event_handlers.register('load-cli-arg', uri_param)
param_shorthand = ParamShorthand()
event_handlers.register('process-cli-arg', param_shorthand)
# The s3 error mesage needs to registered before the
# generic error handler.
register_s3_error_msg(event_handlers)
error_handler = ErrorHandler()
event_handlers.register('after-call.*.*', error_handler)
event_handlers.register('after-call', error_handler)
# # The following will get fired for every option we are
# # documenting. It will attempt to add an example_fn on to
# # the parameter object if the parameter supports shorthand
Expand Down
69 changes: 69 additions & 0 deletions tests/unit/customizations/test_s3errormsg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0e
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import copy

from awscli.testutils import unittest
from awscli.customizations import s3errormsg


class TestGetRegionFromEndpoint(unittest.TestCase):

def test_sigv4_error_message(self):
parsed = {
'Error': {
'Message': 'Please use AWS4-HMAC-SHA256'
}
}
s3errormsg.enhance_error_msg(parsed)
# We should say how to fix the issue.
self.assertIn('You can fix this issue',
parsed['Error']['Message'])
# We should mention the --region argument.
self.assertIn('--region', parsed['Error']['Message'])
# We should mention get-bucket-location
self.assertIn('get-bucket-location', parsed['Error']['Message'])

def test_301_error_message(self):
parsed = {
'Error': {
'Code': 'PermanentRedirect',
'Message': "Please use the correct endpoint.",
'Endpoint': "myendpoint",
}
}
s3errormsg.enhance_error_msg(parsed)
# We should include the endpoint in the error message.
error_message = parsed['Error']['Message']
self.assertIn('myendpoint', error_message)

def test_error_message_not_enhanced(self):
parsed = {
'Error': {
'Message': 'This is a different error message.',
'Code': 'Other Message'
}
}
expected = copy.deepcopy(parsed)
s3errormsg.enhance_error_msg(parsed)
# Nothing should have changed
self.assertEqual(parsed, expected)

def test_not_an_error_message(self):
parsed = {
'Success': 'response',
'ResponseMetadata': {}
}
expected = copy.deepcopy(parsed)
s3errormsg.enhance_error_msg(parsed)
# Nothing should have changed
self.assertEqual(parsed, expected)

0 comments on commit 44f3b0f

Please sign in to comment.