Check for 'Location' header in the response

If the keep_alive is not available because of using reverse
proxy, the header 'Content-Lenght' may not be available,
causing the exception:

 Error communicating with server[...]: empty response

However, if 'Location' header is present, the response is valid.

Add unit tests:
- build_job must pass even if no 'content-lenght'
  in response header
- build_job must fail if no 'location' in response header

Change-Id: I4da6dd19f9d8302a76652a3686a9377f9a2503a6
Closes-Bug:#1775047
This commit is contained in:
Dennis Dmitriev 2018-06-04 21:02:20 +03:00
parent f7fa4ce059
commit f420d6de01
3 changed files with 54 additions and 2 deletions

View File

@ -513,6 +513,7 @@ class Jenkins(object):
headers = response.headers headers = response.headers
if (headers.get('content-length') is None and if (headers.get('content-length') is None and
headers.get('transfer-encoding') is None and headers.get('transfer-encoding') is None and
headers.get('location') is None and
(response.content is None or len(response.content) <= 0)): (response.content is None or len(response.content) <= 0)):
# response body should only exist if one of these is provided # response body should only exist if one of these is provided
raise EmptyResponseException( raise EmptyResponseException(
@ -1274,6 +1275,12 @@ class Jenkins(object):
''' '''
response = self.jenkins_request(requests.Request( response = self.jenkins_request(requests.Request(
'POST', self.build_job_url(name, parameters, token))) 'POST', self.build_job_url(name, parameters, token)))
if 'Location' not in response.headers:
raise EmptyResponseException(
"Header 'Location' not found in "
"response from server[%s]" % self.server)
location = response.headers['Location'] location = response.headers['Location']
# location is a queue item, eg. "http://jenkins/queue/item/25/" # location is a queue item, eg. "http://jenkins/queue/item/25/"
if location.endswith('/'): if location.endswith('/'):

View File

@ -78,14 +78,15 @@ class NullServer(socketserver.TCPServer):
*args, **kwargs) *args, **kwargs)
def build_response_mock(status_code, json_body=None, headers=None, **kwargs): def build_response_mock(status_code, json_body=None, headers=None,
add_content_length=True, **kwargs):
real_response = requests.Response() real_response = requests.Response()
real_response.status_code = status_code real_response.status_code = status_code
text = None text = None
if json_body is not None: if json_body is not None:
text = json.dumps(json_body) text = json.dumps(json_body)
if headers is not {}: if add_content_length and headers is not {}:
real_response.headers['content-length'] = len(text) real_response.headers['content-length'] = len(text)
if headers is not None: if headers is not None:

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from mock import patch from mock import patch
import jenkins
from six.moves.urllib.parse import quote from six.moves.urllib.parse import quote
from tests.helper import build_response_mock from tests.helper import build_response_mock
from tests.jobs.base import JenkinsJobsTestBase from tests.jobs.base import JenkinsJobsTestBase
@ -19,6 +20,49 @@ class JenkinsBuildJobTest(JenkinsJobsTestBase):
self.make_url(quote(u'job/Test Jøb/build'.encode('utf8')))) self.make_url(quote(u'job/Test Jøb/build'.encode('utf8'))))
self.assertEqual(queue_id, 25) self.assertEqual(queue_id, 25)
@patch('jenkins.requests.Session.send', autospec=True)
def test_assert_no_location(self, session_send_mock):
session_send_mock.return_value = build_response_mock(302, {})
with self.assertRaises(jenkins.EmptyResponseException) as context_mgr:
self.j.build_job(u'Test Job')
self.assertEqual(
str(context_mgr.exception),
"Header 'Location' not found in response from server[{0}]".format(
self.make_url('')))
@patch.object(jenkins.Jenkins, 'maybe_add_crumb')
@patch('jenkins.requests.Session.send', autospec=True)
def test_simple_no_content_lenght(self, session_send_mock,
maybe_add_crumb_mock):
maybe_add_crumb_mock.return_value = None
session_send_mock.return_value = build_response_mock(
201, None, add_content_length=False,
headers={'Location': self.make_url('/queue/item/25/')})
queue_id = self.j.build_job(u'Test Job')
self.assertEqual(session_send_mock.call_args[0][1].url,
self.make_url('job/Test%20Job/build'))
self.assertEqual(queue_id, 25)
@patch.object(jenkins.Jenkins, 'maybe_add_crumb')
@patch('jenkins.requests.Session.send', autospec=True)
def test_assert_no_content_lenght_no_location(self, session_send_mock,
maybe_add_crumb_mock):
maybe_add_crumb_mock.return_value = None
session_send_mock.return_value = build_response_mock(
201, None, add_content_length=False)
with self.assertRaises(jenkins.EmptyResponseException) as context_mgr:
self.j.build_job(u'Test Job')
self.assertEqual(
str(context_mgr.exception),
'Error communicating with server[{0}]: empty response'.format(
self.make_url('')))
@patch('jenkins.requests.Session.send', autospec=True) @patch('jenkins.requests.Session.send', autospec=True)
def test_in_folder(self, session_send_mock): def test_in_folder(self, session_send_mock):
session_send_mock.return_value = build_response_mock( session_send_mock.return_value = build_response_mock(