have get_build_artifact support non-json files
remove the json parsing from get_build_artifact to download artifacts not json-formatted and use a stream to download binary artifacts all artifacts are returned as binary string to avoid encoding issues users wishing to parse json artifacts will need to do it outside of this method (alternative could have been to keep get_build_artifact for json and add a new get_binary_build_artifact method but IMHO the json parsing should be done outside) Closes-Bug: #1973243 Change-Id: I24ce4ecd854f8a19ed4d760404adb7d1ac6b5509
This commit is contained in:
parent
d1e4696495
commit
c0bbc66878
|
@ -542,13 +542,13 @@ class Jenkins(object):
|
||||||
# when accessing .text property
|
# when accessing .text property
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _request(self, req):
|
def _request(self, req, stream=None):
|
||||||
|
|
||||||
r = self._session.prepare_request(req)
|
r = self._session.prepare_request(req)
|
||||||
# requests.Session.send() does not honor env settings by design
|
# requests.Session.send() does not honor env settings by design
|
||||||
# see https://github.com/requests/requests/issues/2807
|
# see https://github.com/requests/requests/issues/2807
|
||||||
_settings = self._session.merge_environment_settings(
|
_settings = self._session.merge_environment_settings(
|
||||||
r.url, {}, None, self._session.verify, None)
|
r.url, {}, stream, self._session.verify, None)
|
||||||
_settings['timeout'] = self.timeout
|
_settings['timeout'] = self.timeout
|
||||||
return self._session.send(r, **_settings)
|
return self._session.send(r, **_settings)
|
||||||
|
|
||||||
|
@ -559,7 +559,14 @@ class Jenkins(object):
|
||||||
'''
|
'''
|
||||||
return self.jenkins_request(req, add_crumb, resolve_auth).text
|
return self.jenkins_request(req, add_crumb, resolve_auth).text
|
||||||
|
|
||||||
def jenkins_request(self, req, add_crumb=True, resolve_auth=True):
|
def jenkins_open_stream(self, req, add_crumb=True, resolve_auth=True):
|
||||||
|
'''Return the HTTP response body from a ``requests.Request``.
|
||||||
|
|
||||||
|
:returns: ``stream``
|
||||||
|
'''
|
||||||
|
return self.jenkins_request(req, add_crumb, resolve_auth, True)
|
||||||
|
|
||||||
|
def jenkins_request(self, req, add_crumb=True, resolve_auth=True, stream=None):
|
||||||
'''Utility routine for opening an HTTP request to a Jenkins server.
|
'''Utility routine for opening an HTTP request to a Jenkins server.
|
||||||
|
|
||||||
:param req: A ``requests.Request`` to submit.
|
:param req: A ``requests.Request`` to submit.
|
||||||
|
@ -567,6 +574,7 @@ class Jenkins(object):
|
||||||
before submitting. Defaults to ``True``.
|
before submitting. Defaults to ``True``.
|
||||||
:param resolve_auth: If True, maybe add authentication. Defaults to
|
:param resolve_auth: If True, maybe add authentication. Defaults to
|
||||||
``True``.
|
``True``.
|
||||||
|
:param stream: If True, return a stream
|
||||||
:returns: A ``requests.Response`` object.
|
:returns: A ``requests.Response`` object.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
|
@ -576,7 +584,7 @@ class Jenkins(object):
|
||||||
self.maybe_add_crumb(req)
|
self.maybe_add_crumb(req)
|
||||||
|
|
||||||
return self._response_handler(
|
return self._response_handler(
|
||||||
self._request(req))
|
self._request(req, stream))
|
||||||
|
|
||||||
except req_exc.HTTPError as e:
|
except req_exc.HTTPError as e:
|
||||||
# Jenkins's funky authentication means its nigh impossible to
|
# Jenkins's funky authentication means its nigh impossible to
|
||||||
|
@ -719,23 +727,20 @@ class Jenkins(object):
|
||||||
:param name: Job name, ``str``
|
:param name: Job name, ``str``
|
||||||
:param number: Build number, ``str`` (also accepts ``int``)
|
:param number: Build number, ``str`` (also accepts ``int``)
|
||||||
:param artifact: Artifact relative path, ``str``
|
:param artifact: Artifact relative path, ``str``
|
||||||
:returns: artifact to download, ``dict``
|
:returns: artifact to download, ``bytes``
|
||||||
"""
|
"""
|
||||||
folder_url, short_name = self._get_job_folder(name)
|
folder_url, short_name = self._get_job_folder(name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = self.jenkins_open(requests.Request(
|
with self.jenkins_open_stream(requests.Request(
|
||||||
'GET', self._build_url(BUILD_ARTIFACT, locals())))
|
'GET', self._build_url(BUILD_ARTIFACT, locals()))) as response:
|
||||||
|
if response.encoding is None:
|
||||||
if response:
|
return response.raw.read()
|
||||||
return json.loads(response)
|
|
||||||
else:
|
else:
|
||||||
|
return response.text.encode(response.encoding)
|
||||||
raise JenkinsException('job[%s] number[%s] does not exist' % (name, number))
|
raise JenkinsException('job[%s] number[%s] does not exist' % (name, number))
|
||||||
except requests.exceptions.HTTPError:
|
except requests.exceptions.HTTPError:
|
||||||
raise JenkinsException('job[%s] number[%s] does not exist' % (name, number))
|
raise JenkinsException('job[%s] number[%s] does not exist' % (name, number))
|
||||||
except ValueError:
|
|
||||||
raise JenkinsException(
|
|
||||||
'Could not parse JSON info for job[%s] number[%s]' % (name, number))
|
|
||||||
except NotFoundException:
|
except NotFoundException:
|
||||||
# This can happen if the artifact is not found
|
# This can happen if the artifact is not found
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
from mock import patch
|
from mock import patch, Mock
|
||||||
|
|
||||||
import jenkins
|
import jenkins
|
||||||
from tests.base import JenkinsTestBase
|
from tests.base import JenkinsTestBase
|
||||||
|
@ -723,32 +723,57 @@ class JenkinsBuildTestReportUrlTest(JenkinsTestBase):
|
||||||
|
|
||||||
class JenkinsBuildArtifactUrlTest(JenkinsTestBase):
|
class JenkinsBuildArtifactUrlTest(JenkinsTestBase):
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
def streamMock(self, encoding=None, text=None, binary=None):
|
||||||
|
streamMock = Mock()
|
||||||
|
streamMock.__exit__ = Mock()
|
||||||
|
streamMock.__enter__ = Mock()
|
||||||
|
if encoding is None and text is None and binary is None:
|
||||||
|
streamMock.__enter__.return_value = None
|
||||||
|
return streamMock
|
||||||
|
streamMock.__enter__.return_value = Mock()
|
||||||
|
streamMock.__enter__.return_value.encoding = encoding
|
||||||
|
streamMock.__enter__.return_value.text = text
|
||||||
|
streamMock.__enter__.return_value.raw = Mock()
|
||||||
|
streamMock.__enter__.return_value.raw.read = Mock()
|
||||||
|
streamMock.__enter__.return_value.raw.read.return_value = binary
|
||||||
|
return streamMock
|
||||||
|
|
||||||
|
@patch.object(jenkins.Jenkins, 'jenkins_open_stream')
|
||||||
def test_simple(self, jenkins_mock):
|
def test_simple(self, jenkins_mock):
|
||||||
jenkins_mock.return_value = '{}'
|
jenkins_mock.return_value = self.streamMock('utf-8', 'ascii')
|
||||||
ret = self.j.get_build_artifact(u'Test Job', number='52', artifact="filename")
|
ret = self.j.get_build_artifact(u'Test Job', number='52', artifact="filename")
|
||||||
self.assertEqual(ret, json.loads(jenkins_mock.return_value))
|
self.assertEqual(ret, b'ascii')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
jenkins_mock.call_args[0][0].url,
|
jenkins_mock.call_args[0][0].url,
|
||||||
self.make_url('job/Test%20Job/52/artifact/filename'))
|
self.make_url('job/Test%20Job/52/artifact/filename'))
|
||||||
self._check_requests(jenkins_mock.call_args_list)
|
self._check_requests(jenkins_mock.call_args_list)
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
@patch.object(jenkins.Jenkins, 'jenkins_open_stream')
|
||||||
|
def test_simple_binary(self, jenkins_mock):
|
||||||
|
jenkins_mock.return_value = self.streamMock(binary=b'\0\1\2')
|
||||||
|
ret = self.j.get_build_artifact(u'Test Job', number='52', artifact="filename")
|
||||||
|
self.assertEqual(ret, b'\0\1\2')
|
||||||
|
self.assertEqual(
|
||||||
|
jenkins_mock.call_args[0][0].url,
|
||||||
|
self.make_url('job/Test%20Job/52/artifact/filename'))
|
||||||
|
self._check_requests(jenkins_mock.call_args_list)
|
||||||
|
|
||||||
|
@patch.object(jenkins.Jenkins, 'jenkins_open_stream')
|
||||||
def test_in_folder(self, jenkins_mock):
|
def test_in_folder(self, jenkins_mock):
|
||||||
jenkins_mock.return_value = '{}'
|
jenkins_mock.return_value = self.streamMock('utf-8', 'ascii')
|
||||||
ret = self.j.get_build_artifact(u'a Folder/Test Job', number='52', artifact="file name")
|
ret = self.j.get_build_artifact(u'a Folder/Test Job', number='52', artifact="file name")
|
||||||
self.assertEqual(ret, json.loads(jenkins_mock.return_value))
|
self.assertEqual(ret, b'ascii')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
jenkins_mock.call_args[0][0].url,
|
jenkins_mock.call_args[0][0].url,
|
||||||
self.make_url('job/a%20Folder/job/Test%20Job/52/artifact/file%20name'))
|
self.make_url('job/a%20Folder/job/Test%20Job/52/artifact/file%20name'))
|
||||||
self._check_requests(jenkins_mock.call_args_list)
|
self._check_requests(jenkins_mock.call_args_list)
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
@patch.object(jenkins.Jenkins, 'jenkins_open_stream')
|
||||||
def test_matrix(self, jenkins_mock):
|
def test_matrix(self, jenkins_mock):
|
||||||
jenkins_mock.return_value = '{}'
|
jenkins_mock.return_value = self.streamMock('utf-8', 'ascii')
|
||||||
ret = self.j.get_build_artifact(u'a Folder/Test Job', number='52/index=matrix',
|
ret = self.j.get_build_artifact(u'a Folder/Test Job', number='52/index=matrix',
|
||||||
artifact="file name")
|
artifact="file name")
|
||||||
self.assertEqual(ret, json.loads(jenkins_mock.return_value))
|
self.assertEqual(ret, b'ascii')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
jenkins_mock.call_args[0][0].url,
|
jenkins_mock.call_args[0][0].url,
|
||||||
self.make_url('job/a%20Folder/job/Test%20Job/52/index=matrix/artifact/file%20name'))
|
self.make_url('job/a%20Folder/job/Test%20Job/52/index=matrix/artifact/file%20name'))
|
||||||
|
@ -763,10 +788,9 @@ class JenkinsBuildArtifactUrlTest(JenkinsTestBase):
|
||||||
ret = self.j.get_build_artifact(u'TestJob', number='52', artifact="filename")
|
ret = self.j.get_build_artifact(u'TestJob', number='52', artifact="filename")
|
||||||
self.assertIsNone(ret)
|
self.assertIsNone(ret)
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
@patch.object(jenkins.Jenkins, 'jenkins_open_stream')
|
||||||
def test_open_return_none(self, jenkins_mock):
|
def test_open_return_none(self, jenkins_mock):
|
||||||
jenkins_mock.return_value = None
|
jenkins_mock.return_value = self.streamMock()
|
||||||
|
|
||||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||||
self.j.get_build_artifact(u'TestJob', number='52', artifact="filename")
|
self.j.get_build_artifact(u'TestJob', number='52', artifact="filename")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -774,17 +798,6 @@ class JenkinsBuildArtifactUrlTest(JenkinsTestBase):
|
||||||
'job[TestJob] number[52] does not exist')
|
'job[TestJob] number[52] does not exist')
|
||||||
self._check_requests(jenkins_mock.call_args_list)
|
self._check_requests(jenkins_mock.call_args_list)
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
|
||||||
def test_return_invalid_json(self, jenkins_mock):
|
|
||||||
jenkins_mock.return_value = 'Invalid JSON'
|
|
||||||
|
|
||||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
|
||||||
self.j.get_build_artifact(u'TestJob', number='52', artifact="filename")
|
|
||||||
self.assertEqual(
|
|
||||||
str(context_manager.exception),
|
|
||||||
'Could not parse JSON info for job[TestJob] number[52]')
|
|
||||||
self._check_requests(jenkins_mock.call_args_list)
|
|
||||||
|
|
||||||
@patch('jenkins.requests.Session.send', autospec=True)
|
@patch('jenkins.requests.Session.send', autospec=True)
|
||||||
def test_raise_HTTPError(self, session_send_mock):
|
def test_raise_HTTPError(self, session_send_mock):
|
||||||
session_send_mock.side_effect = iter([
|
session_send_mock.side_effect = iter([
|
||||||
|
|
Loading…
Reference in New Issue