detect and respect http redirects
Detects if API url is redirected and corrects it in order to avoid doubling number of requests and even failing POST ones because of having them silently converted by requests from POST to GET. Displays a warning to the user when redirection occurs because almost always this means that they are using an old endpoint, like a non secured one. Change-Id: I7387bf150dad307342f9a6a91afbae32859bc82e Signed-off-by: Sorin Sbarnea <ssbarnea@redhat.com>
This commit is contained in:
parent
4e64f0f880
commit
4150a83d45
@ -287,7 +287,7 @@ class Jenkins(object):
|
|||||||
_timeout_warning_issued = False
|
_timeout_warning_issued = False
|
||||||
|
|
||||||
def __init__(self, url, username=None, password=None,
|
def __init__(self, url, username=None, password=None,
|
||||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
|
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, resolve=True):
|
||||||
'''Create handle to Jenkins instance.
|
'''Create handle to Jenkins instance.
|
||||||
|
|
||||||
All methods will raise :class:`JenkinsException` on failure.
|
All methods will raise :class:`JenkinsException` on failure.
|
||||||
@ -296,6 +296,7 @@ class Jenkins(object):
|
|||||||
:param username: Server username, ``str``
|
:param username: Server username, ``str``
|
||||||
:param password: Server password, ``str``
|
:param password: Server password, ``str``
|
||||||
:param timeout: Server connection timeout in secs (default: not set), ``int``
|
:param timeout: Server connection timeout in secs (default: not set), ``int``
|
||||||
|
:param resolve: Attempts to resolve and auto-correct API redirection. default: True ``bool``
|
||||||
'''
|
'''
|
||||||
if url[-1] == '/':
|
if url[-1] == '/':
|
||||||
self.server = url
|
self.server = url
|
||||||
@ -328,6 +329,25 @@ class Jenkins(object):
|
|||||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
self._session.verify = False
|
self._session.verify = False
|
||||||
|
|
||||||
|
if resolve:
|
||||||
|
self._resolve_api()
|
||||||
|
|
||||||
|
def _resolve_api(self):
|
||||||
|
'''Detects if Jenkins api frontend is redirect and corrects it if so
|
||||||
|
in order to avoid future redirects of each request or failures caused
|
||||||
|
by the fact that a redirected POST is transformed into a GET.
|
||||||
|
'''
|
||||||
|
r = self.jenkins_request(requests.Request('HEAD', self.server), add_crumb=False)
|
||||||
|
if r.url != self.server or r.status_code in [300, 301, 302, 303]:
|
||||||
|
if 'Location' in r.headers:
|
||||||
|
new_url = r.headers['Location']
|
||||||
|
else:
|
||||||
|
new_url = r.url
|
||||||
|
warnings.warn(
|
||||||
|
"Redirection from %s to %s detected, you may want to update your frontend url." % (
|
||||||
|
self.server, new_url))
|
||||||
|
self.server = new_url
|
||||||
|
|
||||||
def _get_encoded_params(self, params):
|
def _get_encoded_params(self, params):
|
||||||
for k, v in params.items():
|
for k, v in params.items():
|
||||||
if k in ["name", "msg", "short_name", "from_short_name",
|
if k in ["name", "msg", "short_name", "from_short_name",
|
||||||
|
@ -30,7 +30,7 @@ class JenkinsTestBase(TestWithScenarios, unittest.TestCase):
|
|||||||
'jenkins.requests_kerberos', None)
|
'jenkins.requests_kerberos', None)
|
||||||
self.request_kerberos_module_patcher.start()
|
self.request_kerberos_module_patcher.start()
|
||||||
|
|
||||||
self.j = jenkins.Jenkins(self.base_url, 'test', 'test')
|
self.j = jenkins.Jenkins(self.base_url, 'test', 'test', resolve=False)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class JenkinsConstructorTest(JenkinsTestBase):
|
|||||||
self.assertEqual(self.j.crumb, None)
|
self.assertEqual(self.j.crumb, None)
|
||||||
|
|
||||||
def test_url_without_trailing_slash(self):
|
def test_url_without_trailing_slash(self):
|
||||||
j = jenkins.Jenkins(self.base_url, 'test', 'test')
|
j = jenkins.Jenkins(self.base_url, 'test', 'test', resolve=False)
|
||||||
j._maybe_add_auth()
|
j._maybe_add_auth()
|
||||||
self.assertEqual(j.server, self.make_url(''))
|
self.assertEqual(j.server, self.make_url(''))
|
||||||
self.assertEqual(j.auth(self.req).headers['Authorization'],
|
self.assertEqual(j.auth(self.req).headers['Authorization'],
|
||||||
@ -38,7 +38,7 @@ class JenkinsConstructorTest(JenkinsTestBase):
|
|||||||
self.assertEqual(j.crumb, None)
|
self.assertEqual(j.crumb, None)
|
||||||
|
|
||||||
def test_without_user_or_password(self):
|
def test_without_user_or_password(self):
|
||||||
j = jenkins.Jenkins('{0}'.format(self.base_url))
|
j = jenkins.Jenkins('{0}'.format(self.base_url), resolve=False)
|
||||||
j._maybe_add_auth()
|
j._maybe_add_auth()
|
||||||
self.assertEqual(j.server, self.make_url(''))
|
self.assertEqual(j.server, self.make_url(''))
|
||||||
self.assertEqual(j.auth, None)
|
self.assertEqual(j.auth, None)
|
||||||
@ -47,7 +47,8 @@ class JenkinsConstructorTest(JenkinsTestBase):
|
|||||||
def test_unicode_password(self):
|
def test_unicode_password(self):
|
||||||
j = jenkins.Jenkins('{0}'.format(self.base_url),
|
j = jenkins.Jenkins('{0}'.format(self.base_url),
|
||||||
six.u('nonascii'),
|
six.u('nonascii'),
|
||||||
six.u('\xe9\u20ac'))
|
six.u('\xe9\u20ac'),
|
||||||
|
resolve=False)
|
||||||
j._maybe_add_auth()
|
j._maybe_add_auth()
|
||||||
self.assertEqual(j.server, self.make_url(''))
|
self.assertEqual(j.server, self.make_url(''))
|
||||||
self.assertEqual(j.auth(self.req).headers['Authorization'],
|
self.assertEqual(j.auth(self.req).headers['Authorization'],
|
||||||
@ -58,7 +59,7 @@ class JenkinsConstructorTest(JenkinsTestBase):
|
|||||||
long_str = 'a' * 60
|
long_str = 'a' * 60
|
||||||
long_str_b64 = 'YWFh' * 20
|
long_str_b64 = 'YWFh' * 20
|
||||||
|
|
||||||
j = jenkins.Jenkins('{0}'.format(self.base_url), long_str, long_str)
|
j = jenkins.Jenkins('{0}'.format(self.base_url), long_str, long_str, resolve=False)
|
||||||
j._maybe_add_auth()
|
j._maybe_add_auth()
|
||||||
|
|
||||||
auth_header = j.auth(self.req).headers['Authorization']
|
auth_header = j.auth(self.req).headers['Authorization']
|
||||||
@ -67,11 +68,11 @@ class JenkinsConstructorTest(JenkinsTestBase):
|
|||||||
long_str_b64 + 'Om' + long_str_b64[2:] + 'YQ=='))
|
long_str_b64 + 'Om' + long_str_b64[2:] + 'YQ=='))
|
||||||
|
|
||||||
def test_default_timeout(self):
|
def test_default_timeout(self):
|
||||||
j = jenkins.Jenkins('{0}'.format(self.base_url))
|
j = jenkins.Jenkins('{0}'.format(self.base_url), resolve=False)
|
||||||
self.assertEqual(j.timeout, socket._GLOBAL_DEFAULT_TIMEOUT)
|
self.assertEqual(j.timeout, socket._GLOBAL_DEFAULT_TIMEOUT)
|
||||||
|
|
||||||
def test_custom_timeout(self):
|
def test_custom_timeout(self):
|
||||||
j = jenkins.Jenkins('{0}'.format(self.base_url), timeout=300)
|
j = jenkins.Jenkins('{0}'.format(self.base_url), timeout=300, resolve=False)
|
||||||
self.assertEqual(j.timeout, 300)
|
self.assertEqual(j.timeout, 300)
|
||||||
|
|
||||||
|
|
||||||
@ -206,7 +207,9 @@ class JenkinsOpenTest(JenkinsTestBase):
|
|||||||
def test_timeout(self, session_send_mock):
|
def test_timeout(self, session_send_mock):
|
||||||
session_send_mock.side_effect = jenkins.URLError(
|
session_send_mock.side_effect = jenkins.URLError(
|
||||||
reason="timed out")
|
reason="timed out")
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', timeout=1)
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test',
|
||||||
|
timeout=1,
|
||||||
|
resolve=False)
|
||||||
request = jenkins.requests.Request('GET', self.make_url('job/TestJob'))
|
request = jenkins.requests.Request('GET', self.make_url('job/TestJob'))
|
||||||
|
|
||||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||||
@ -223,7 +226,7 @@ class JenkinsOpenTest(JenkinsTestBase):
|
|||||||
@patch.object(jenkins.Jenkins, 'get_version',
|
@patch.object(jenkins.Jenkins, 'get_version',
|
||||||
return_value="Version42")
|
return_value="Version42")
|
||||||
def test_wait_for_normal_op(self, version_mock, jenkins_mock):
|
def test_wait_for_normal_op(self, version_mock, jenkins_mock):
|
||||||
j = jenkins.Jenkins('http://example.com', 'test', 'test')
|
j = jenkins.Jenkins('http://example.com', 'test', 'test', resolve=False)
|
||||||
self.assertTrue(j.wait_for_normal_op(0))
|
self.assertTrue(j.wait_for_normal_op(0))
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open',
|
@patch.object(jenkins.Jenkins, 'jenkins_open',
|
||||||
@ -231,11 +234,11 @@ class JenkinsOpenTest(JenkinsTestBase):
|
|||||||
@patch.object(jenkins.Jenkins, 'get_version',
|
@patch.object(jenkins.Jenkins, 'get_version',
|
||||||
side_effect=jenkins.EmptyResponseException())
|
side_effect=jenkins.EmptyResponseException())
|
||||||
def test_wait_for_normal_op__empty_response(self, version_mock, jenkins_mock):
|
def test_wait_for_normal_op__empty_response(self, version_mock, jenkins_mock):
|
||||||
j = jenkins.Jenkins('http://example.com', 'test', 'test')
|
j = jenkins.Jenkins('http://example.com', 'test', 'test', resolve=False)
|
||||||
self.assertFalse(j.wait_for_normal_op(0))
|
self.assertFalse(j.wait_for_normal_op(0))
|
||||||
|
|
||||||
def test_wait_for_normal_op__negative_timeout(self):
|
def test_wait_for_normal_op__negative_timeout(self):
|
||||||
j = jenkins.Jenkins('http://example.com', 'test', 'test')
|
j = jenkins.Jenkins('http://example.com', 'test', 'test', resolve=False)
|
||||||
with self.assertRaises(ValueError) as context_manager:
|
with self.assertRaises(ValueError) as context_manager:
|
||||||
j.wait_for_normal_op(-1)
|
j.wait_for_normal_op(-1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -22,7 +22,7 @@ class JenkinsRequestTimeoutTests(testtools.TestCase):
|
|||||||
|
|
||||||
def test_jenkins_open_timeout(self):
|
def test_jenkins_open_timeout(self):
|
||||||
j = jenkins.Jenkins("http://%s:%s" % self.server.server_address,
|
j = jenkins.Jenkins("http://%s:%s" % self.server.server_address,
|
||||||
None, None, timeout=0.1)
|
None, None, timeout=0.1, resolve=False)
|
||||||
request = jenkins.requests.Request('GET', 'http://%s:%s/job/TestJob' %
|
request = jenkins.requests.Request('GET', 'http://%s:%s/job/TestJob' %
|
||||||
self.server.server_address)
|
self.server.server_address)
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ class JenkinsRequestTimeoutTests(testtools.TestCase):
|
|||||||
|
|
||||||
def test_jenkins_open_no_timeout(self):
|
def test_jenkins_open_no_timeout(self):
|
||||||
j = jenkins.Jenkins("http://%s:%s" % self.server.server_address,
|
j = jenkins.Jenkins("http://%s:%s" % self.server.server_address,
|
||||||
None, None)
|
None, None, resolve=False)
|
||||||
request = jenkins.requests.Request('GET', 'http://%s:%s/job/TestJob' %
|
request = jenkins.requests.Request('GET', 'http://%s:%s/job/TestJob' %
|
||||||
self.server.server_address)
|
self.server.server_address)
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ class JenkinsPluginInfoTest(JenkinsPluginsBase):
|
|||||||
json.dumps(self.plugin_info_json),
|
json.dumps(self.plugin_info_json),
|
||||||
json.dumps(self.updated_plugin_info_json)
|
json.dumps(self.updated_plugin_info_json)
|
||||||
]
|
]
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test')
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', resolve=False)
|
||||||
|
|
||||||
plugins_info = j.get_plugins()
|
plugins_info = j.get_plugins()
|
||||||
self.assertEqual(plugins_info["mailer"]["version"],
|
self.assertEqual(plugins_info["mailer"]["version"],
|
||||||
@ -289,7 +289,7 @@ class PluginsTestScenarios(JenkinsPluginsBase):
|
|||||||
equality operator defined for the scenario.
|
equality operator defined for the scenario.
|
||||||
"""
|
"""
|
||||||
plugin_name = "Jenkins Mailer Plugin"
|
plugin_name = "Jenkins Mailer Plugin"
|
||||||
j = jenkins.Jenkins(self.base_url, 'test', 'test')
|
j = jenkins.Jenkins(self.base_url, 'test', 'test', resolve=False)
|
||||||
plugin_info = j.get_plugins()[plugin_name]
|
plugin_info = j.get_plugins()[plugin_name]
|
||||||
v1 = plugin_info.get("version")
|
v1 = plugin_info.get("version")
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ class PluginsTestScenarios(JenkinsPluginsBase):
|
|||||||
type of PluginVersion before comparing provides the same result.
|
type of PluginVersion before comparing provides the same result.
|
||||||
"""
|
"""
|
||||||
plugin_name = "Jenkins Mailer Plugin"
|
plugin_name = "Jenkins Mailer Plugin"
|
||||||
j = jenkins.Jenkins(self.base_url, 'test', 'test')
|
j = jenkins.Jenkins(self.base_url, 'test', 'test', resolve=False)
|
||||||
plugin_info = j.get_plugins()[plugin_name]
|
plugin_info = j.get_plugins()[plugin_name]
|
||||||
v1 = plugin_info.get("version")
|
v1 = plugin_info.get("version")
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class JenkinsScriptTest(JenkinsTestBase):
|
|||||||
def test_install_plugin(self, jenkins_mock):
|
def test_install_plugin(self, jenkins_mock):
|
||||||
'''Installation of plugins is done with the run_script method
|
'''Installation of plugins is done with the run_script method
|
||||||
'''
|
'''
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test')
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', resolve=False)
|
||||||
j.install_plugin("jabber")
|
j.install_plugin("jabber")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
jenkins_mock.call_args[0][0].url,
|
jenkins_mock.call_args[0][0].url,
|
||||||
@ -49,7 +49,7 @@ class JenkinsScriptTest(JenkinsTestBase):
|
|||||||
def test_install_plugin_with_dependencies(self, run_script_mock, jenkins_mock):
|
def test_install_plugin_with_dependencies(self, run_script_mock, jenkins_mock):
|
||||||
'''Verify install plugins with dependencies
|
'''Verify install plugins with dependencies
|
||||||
'''
|
'''
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test')
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', resolve=False)
|
||||||
j.install_plugin("jabber")
|
j.install_plugin("jabber")
|
||||||
self.assertEqual(len(run_script_mock.call_args_list), 2)
|
self.assertEqual(len(run_script_mock.call_args_list), 2)
|
||||||
self.assertEqual(run_script_mock.call_args_list[0][0][0],
|
self.assertEqual(run_script_mock.call_args_list[0][0][0],
|
||||||
@ -65,7 +65,7 @@ class JenkinsScriptTest(JenkinsTestBase):
|
|||||||
def test_install_plugin_without_dependencies(self, run_script_mock, jenkins_mock):
|
def test_install_plugin_without_dependencies(self, run_script_mock, jenkins_mock):
|
||||||
'''Verify install plugins without dependencies
|
'''Verify install plugins without dependencies
|
||||||
'''
|
'''
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test')
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', resolve=False)
|
||||||
j.install_plugin("jabber", include_dependencies=False)
|
j.install_plugin("jabber", include_dependencies=False)
|
||||||
self.assertEqual(len(run_script_mock.call_args_list), 2)
|
self.assertEqual(len(run_script_mock.call_args_list), 2)
|
||||||
self.assertEqual(run_script_mock.call_args_list[0][0][0],
|
self.assertEqual(run_script_mock.call_args_list[0][0][0],
|
||||||
@ -81,7 +81,7 @@ class JenkinsScriptTest(JenkinsTestBase):
|
|||||||
'''Verify install plugin does not need a restart
|
'''Verify install plugin does not need a restart
|
||||||
'''
|
'''
|
||||||
run_script_mock.return_value = u'Result: false\n'
|
run_script_mock.return_value = u'Result: false\n'
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test')
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', resolve=False)
|
||||||
self.assertFalse(j.install_plugin("jabber"))
|
self.assertFalse(j.install_plugin("jabber"))
|
||||||
|
|
||||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||||
@ -90,5 +90,5 @@ class JenkinsScriptTest(JenkinsTestBase):
|
|||||||
'''Verify install plugin needs a restart
|
'''Verify install plugin needs a restart
|
||||||
'''
|
'''
|
||||||
run_script_mock.return_value = u'Result: true\n'
|
run_script_mock.return_value = u'Result: true\n'
|
||||||
j = jenkins.Jenkins(self.make_url(''), 'test', 'test')
|
j = jenkins.Jenkins(self.make_url(''), 'test', 'test', resolve=False)
|
||||||
self.assertTrue(j.install_plugin("jabber"))
|
self.assertTrue(j.install_plugin("jabber"))
|
||||||
|
Loading…
Reference in New Issue
Block a user