Prevent down nodes failing PUTs with non-ascii obj names

On an object PUT with a non-ascii name, if we hit some kind of
exception speaking to only one object-server of the N we try to
connect to, we try to log it -- but this causes an exception when
interpolating the UTF-8 encoded path iff the message template is
unicode.

Since this is essentially an exception within an exception handler,
this fails the entire request with a 500 error -- even though the
other nodes may have been just fine. This occurs before it attempts
a handoff node.

The simplest way to reproduce this is by running func tests against
a small cluster where one of the object nodes is not running

N.B. The locale of the node does not matter because the message
template is interpolated with node/device data from the Ring which is
always unicode because of json.

This includes an update to the FakeRing used by unittest
infrastructure to ensure that the FakeRing devices make a round-trip
through json to ensure consistent typing with real Rings.

Change-Id: Icb7284eb5abc9869c1620ee6366817112d8e5587
Closes-bug: #1597210
This commit is contained in:
Brian Cline 2016-06-29 03:32:09 -05:00 committed by Clay Gerrard
parent 79be80f126
commit 7568ea5dd9
3 changed files with 19 additions and 3 deletions

View File

@ -535,7 +535,7 @@ class Application(object):
' re: %(info)s'), ' re: %(info)s'),
{'type': typ, 'ip': node['ip'], {'type': typ, 'ip': node['ip'],
'port': node['port'], 'device': node['device'], 'port': node['port'], 'device': node['device'],
'info': additional_info}, 'info': additional_info.decode('utf-8')},
**kwargs) **kwargs)
def modify_wsgi_pipeline(self, pipe): def modify_wsgi_pipeline(self, pipe):

View File

@ -32,6 +32,7 @@ import eventlet
from eventlet.green import socket from eventlet.green import socket
from tempfile import mkdtemp from tempfile import mkdtemp
from shutil import rmtree from shutil import rmtree
import json
from swift.common.utils import Timestamp, NOTICE from swift.common.utils import Timestamp, NOTICE
@ -223,7 +224,8 @@ class FakeRing(Ring):
for x in range(self.replicas): for x in range(self.replicas):
ip = '10.0.0.%s' % x ip = '10.0.0.%s' % x
port = self._base_port + x port = self._base_port + x
self._devs.append({ # round trip through json to ensure unicode like real rings
self._devs.append(json.loads(json.dumps({
'ip': ip, 'ip': ip,
'replication_ip': ip, 'replication_ip': ip,
'port': port, 'port': port,
@ -232,7 +234,7 @@ class FakeRing(Ring):
'zone': x % 3, 'zone': x % 3,
'region': x % 2, 'region': x % 2,
'id': x, 'id': x,
}) })))
@property @property
def replica_count(self): def replica_count(self):

View File

@ -856,6 +856,20 @@ class TestReplicatedObjController(BaseObjectControllerMixin,
node_error_count(self.app, object_ring.devs[1]), node_error_count(self.app, object_ring.devs[1]),
self.app.error_suppression_limit + 1) self.app.error_suppression_limit + 1)
def test_PUT_connect_exception_with_unicode_path_and_locale(self):
expected = 201
statuses = (
Exception('Connection refused: Please insert ten dollars'),
201, 201)
req = swob.Request.blank('/v1/AUTH_kilroy/%ED%88%8E/%E9%90%89',
method='PUT',
body='life is utf-gr8')
with set_http_connect(*statuses):
resp = req.get_response(self.app)
self.assertEqual(resp.status_int, expected)
def test_PUT_error_during_transfer_data(self): def test_PUT_error_during_transfer_data(self):
class FakeReader(object): class FakeReader(object):
def read(self, size): def read(self, size):