From d124ce5792d93e1efcdb083211ecef381f7b7173 Mon Sep 17 00:00:00 2001 From: Clay Gerrard Date: Thu, 25 Jun 2015 01:35:07 -0700 Subject: [PATCH] Fix ValueError in ssync_receiver httplib's putheader method will cast whatever you give it to a string. where we allow the default dict.get default of None to be passed to putheader unmodified ssync_receiver is surpised that the non-empty string isn't able to be converted to an integer. We can avoid surprising the ssync_receiver in this way by sending the empty string as a better default. Change-Id: Ie9df9927ff4d3dd3f334647f883b2937d0d81030 --- swift/obj/ssync_sender.py | 4 +- test/unit/obj/test_ssync_sender.py | 69 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/swift/obj/ssync_sender.py b/swift/obj/ssync_sender.py index 50662da84c..0657b0fd59 100644 --- a/swift/obj/ssync_sender.py +++ b/swift/obj/ssync_sender.py @@ -134,10 +134,10 @@ class Sender(object): # will be rebuilding them self.connection.putheader( 'X-Backend-Ssync-Frag-Index', self.node.get( - 'index', self.job.get('frag_index'))) + 'index', self.job.get('frag_index', ''))) # a revert job to a handoff will not have a node index self.connection.putheader('X-Backend-Ssync-Node-Index', - self.node.get('index')) + self.node.get('index', '')) self.connection.endheaders() with exceptions.MessageTimeout( self.daemon.node_timeout, 'connect receive'): diff --git a/test/unit/obj/test_ssync_sender.py b/test/unit/obj/test_ssync_sender.py index b6f75ba659..11cd06f22e 100644 --- a/test/unit/obj/test_ssync_sender.py +++ b/test/unit/obj/test_ssync_sender.py @@ -272,6 +272,75 @@ class TestSender(BaseTestSender): method_name, mock_method.mock_calls, expected_calls)) + def test_connect_handoff(self): + node = dict(replication_ip='1.2.3.4', replication_port=5678, + device='sda1') + job = dict(partition='9', policy=POLICIES[1], frag_index=9) + self.sender = ssync_sender.Sender(self.daemon, node, job, None) + self.sender.suffixes = ['abc'] + with mock.patch( + 'swift.obj.ssync_sender.bufferedhttp.BufferedHTTPConnection' + ) as mock_conn_class: + mock_conn = mock_conn_class.return_value + mock_resp = mock.MagicMock() + mock_resp.status = 200 + mock_conn.getresponse.return_value = mock_resp + self.sender.connect() + mock_conn_class.assert_called_once_with('1.2.3.4:5678') + expectations = { + 'putrequest': [ + mock.call('SSYNC', '/sda1/9'), + ], + 'putheader': [ + mock.call('Transfer-Encoding', 'chunked'), + mock.call('X-Backend-Storage-Policy-Index', 1), + mock.call('X-Backend-Ssync-Frag-Index', 9), + mock.call('X-Backend-Ssync-Node-Index', ''), + ], + 'endheaders': [mock.call()], + } + for method_name, expected_calls in expectations.items(): + mock_method = getattr(mock_conn, method_name) + self.assertEquals(expected_calls, mock_method.mock_calls, + 'connection method "%s" got %r not %r' % ( + method_name, mock_method.mock_calls, + expected_calls)) + + def test_connect_handoff_replicated(self): + node = dict(replication_ip='1.2.3.4', replication_port=5678, + device='sda1') + # no frag_index in rsync job + job = dict(partition='9', policy=POLICIES[1]) + self.sender = ssync_sender.Sender(self.daemon, node, job, None) + self.sender.suffixes = ['abc'] + with mock.patch( + 'swift.obj.ssync_sender.bufferedhttp.BufferedHTTPConnection' + ) as mock_conn_class: + mock_conn = mock_conn_class.return_value + mock_resp = mock.MagicMock() + mock_resp.status = 200 + mock_conn.getresponse.return_value = mock_resp + self.sender.connect() + mock_conn_class.assert_called_once_with('1.2.3.4:5678') + expectations = { + 'putrequest': [ + mock.call('SSYNC', '/sda1/9'), + ], + 'putheader': [ + mock.call('Transfer-Encoding', 'chunked'), + mock.call('X-Backend-Storage-Policy-Index', 1), + mock.call('X-Backend-Ssync-Frag-Index', ''), + mock.call('X-Backend-Ssync-Node-Index', ''), + ], + 'endheaders': [mock.call()], + } + for method_name, expected_calls in expectations.items(): + mock_method = getattr(mock_conn, method_name) + self.assertEquals(expected_calls, mock_method.mock_calls, + 'connection method "%s" got %r not %r' % ( + method_name, mock_method.mock_calls, + expected_calls)) + def test_call(self): def patch_sender(sender): sender.connect = mock.MagicMock()