lib: retry openRemoteFile and check size
This commit is contained in:
parent
360ccd248a
commit
8ea4a7245b
2 changed files with 50 additions and 1 deletions
|
|
@ -55,6 +55,8 @@ import six
|
|||
import six.moves.configparser
|
||||
import six.moves.http_client
|
||||
import six.moves.urllib
|
||||
from requests.adapters import HTTPAdapter
|
||||
from requests.packages.urllib3.util.retry import Retry
|
||||
from six.moves import range, zip
|
||||
|
||||
from koji.xmlrpcplus import Fault, dumps, getparser, loads, xmlrpc_client
|
||||
|
|
@ -1748,6 +1750,18 @@ def format_exc_plus():
|
|||
return rv
|
||||
|
||||
|
||||
def request_with_retry(retries=3, backoff_factor=0.3,
|
||||
status_forcelist=(500, 502, 504, 408, 429), session=None):
|
||||
# stolen from https://www.peterbe.com/plog/best-practice-with-retries-with-requests
|
||||
session = session or requests.Session()
|
||||
retry = Retry(total=retries, read=retries, connect=retries,
|
||||
backoff_factor=backoff_factor,
|
||||
status_forcelist=status_forcelist)
|
||||
adapter = HTTPAdapter(max_retries=retry)
|
||||
session.mount('http://', adapter)
|
||||
session.mount('https://', adapter)
|
||||
return session
|
||||
|
||||
def openRemoteFile(relpath, topurl=None, topdir=None, tempdir=None):
|
||||
"""Open a file on the main server (read-only)
|
||||
|
||||
|
|
@ -1756,12 +1770,15 @@ def openRemoteFile(relpath, topurl=None, topdir=None, tempdir=None):
|
|||
if topurl:
|
||||
url = "%s/%s" % (topurl, relpath)
|
||||
fo = tempfile.TemporaryFile(dir=tempdir)
|
||||
resp = requests.get(url, stream=True)
|
||||
resp = request_with_retry().get(url, stream=True)
|
||||
try:
|
||||
for chunk in resp.iter_content(chunk_size=8192):
|
||||
fo.write(chunk)
|
||||
finally:
|
||||
resp.close()
|
||||
if resp.headers.get('Content-Length') and fo.tell() != int(resp.headers['Content-Length']):
|
||||
raise GenericError("Downloaded file %s doesn't match expected size (%s vs %s)" %
|
||||
(url, fo.tell(), resp.headers['Content-Length']))
|
||||
fo.seek(0)
|
||||
elif topdir:
|
||||
fn = "%s/%s" % (topdir, relpath)
|
||||
|
|
|
|||
|
|
@ -161,6 +161,38 @@ class MiscFunctionTestCase(unittest.TestCase):
|
|||
for m in mocks:
|
||||
m.assert_not_called()
|
||||
|
||||
for m in mocks:
|
||||
m.reset_mock()
|
||||
|
||||
# downloaded size is larger than content-length
|
||||
with requests_mock.Mocker() as m_requests:
|
||||
text = 'random content'
|
||||
m_requests.register_uri('GET', url, text=text,
|
||||
headers = {'Content-Length': "3"})
|
||||
m_TemporaryFile.return_value.tell.return_value = len(text)
|
||||
# using neither
|
||||
with self.assertRaises(koji.GenericError):
|
||||
koji.openRemoteFile(path, topurl=topurl)
|
||||
m_TemporaryFile.assert_called_once()
|
||||
m_TemporaryFile.return_value.tell.assert_called()
|
||||
m_open.assert_not_called()
|
||||
|
||||
for m in mocks:
|
||||
m.reset_mock()
|
||||
|
||||
# downloaded size is shorter than content-length
|
||||
with requests_mock.Mocker() as m_requests:
|
||||
text = 'random content'
|
||||
m_requests.register_uri('GET', url, text=text,
|
||||
headers = {'Content-Length': "100"})
|
||||
m_TemporaryFile.return_value.tell.return_value = len(text)
|
||||
# using neither
|
||||
with self.assertRaises(koji.GenericError):
|
||||
koji.openRemoteFile(path, topurl=topurl)
|
||||
m_TemporaryFile.assert_called_once()
|
||||
m_TemporaryFile.return_value.tell.assert_called()
|
||||
m_open.assert_not_called()
|
||||
|
||||
def test_joinpath_bad(self):
|
||||
bad_joins = [
|
||||
['/foo', '../bar'],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue