Allow to create a rest_client not following redirects

Some tests need to verify that the first response from an API is indeed
a redirect, this can not be done if the client automatically follows
redirects. Introduce a parameter that allows consumers to disable the
default behaviour, so that they can see the 301 response instead.

Change-Id: I366fa8d1971cd7502a1cd985f5ee6ad5e1ecb216
Closes-Bug: 1616892
diff --git a/releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml b/releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml
new file mode 100644
index 0000000..f245dcb
--- /dev/null
+++ b/releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml
@@ -0,0 +1,16 @@
+---
+features:
+  - |
+    A new parameter ``follow_redirects`` has been added to the class
+    ``RestClient``, which is passed through to ``ClosingHttp`` or
+    ``ClosingProxyHttp`` respectively. The default value is ``True``
+    which corresponds to the previous behaviour of following up to five
+    redirections before returning a response. Setting
+    ``follow_redirects = False`` allows to disable this behaviour, so
+    that any redirect that is received is directly returned to the caller.
+    This allows tests to verify that an API is responding with a redirect.
+fixes:
+  - |
+    [`bug 1616892 <https://bugs.launchpad.net/tempest/+bug/1616892>`_]
+    Tempest now allows tests to verify that an API responds with a
+    redirect.
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index 738c37f..8c1a802 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -19,7 +19,8 @@
 
 class ClosingProxyHttp(urllib3.ProxyManager):
     def __init__(self, proxy_url, disable_ssl_certificate_validation=False,
-                 ca_certs=None, timeout=None):
+                 ca_certs=None, timeout=None, follow_redirects=True):
+        self.follow_redirects = follow_redirects
         kwargs = {}
 
         if disable_ssl_certificate_validation:
@@ -50,9 +51,14 @@
         new_headers = dict(original_headers, connection='close')
         new_kwargs = dict(kwargs, headers=new_headers)
 
-        # Follow up to 5 redirections. Don't raise an exception if
-        # it's exceeded but return the HTTP 3XX response instead.
-        retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+        if self.follow_redirects:
+            # Follow up to 5 redirections. Don't raise an exception if
+            # it's exceeded but return the HTTP 3XX response instead.
+            retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+        else:
+            # Do not follow redirections. Don't raise an exception if
+            # a redirect is found, but return the HTTP 3XX response instead.
+            retry = urllib3.util.Retry(redirect=False)
         r = super(ClosingProxyHttp, self).request(method, url, retries=retry,
                                                   *args, **new_kwargs)
         return Response(r), r.data
@@ -60,7 +66,8 @@
 
 class ClosingHttp(urllib3.poolmanager.PoolManager):
     def __init__(self, disable_ssl_certificate_validation=False,
-                 ca_certs=None, timeout=None):
+                 ca_certs=None, timeout=None, follow_redirects=True):
+        self.follow_redirects = follow_redirects
         kwargs = {}
 
         if disable_ssl_certificate_validation:
@@ -93,9 +100,14 @@
         new_headers = dict(original_headers, connection='close')
         new_kwargs = dict(kwargs, headers=new_headers)
 
-        # Follow up to 5 redirections. Don't raise an exception if
-        # it's exceeded but return the HTTP 3XX response instead.
-        retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+        if self.follow_redirects:
+            # Follow up to 5 redirections. Don't raise an exception if
+            # it's exceeded but return the HTTP 3XX response instead.
+            retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+        else:
+            # Do not follow redirections. Don't raise an exception if
+            # a redirect is found, but return the HTTP 3XX response instead.
+            retry = urllib3.util.Retry(redirect=False)
         r = super(ClosingHttp, self).request(method, url, retries=retry,
                                              *args, **new_kwargs)
         return Response(r), r.data
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 22276d4..fd1c91e 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -70,6 +70,7 @@
     :param str http_timeout: Timeout in seconds to wait for the http request to
                              return
     :param str proxy_url: http proxy url to use.
+    :param bool follow_redirects: Set to false to stop following redirects.
     """
 
     # The version of the API this client implements
@@ -82,7 +83,7 @@
                  build_interval=1, build_timeout=60,
                  disable_ssl_certificate_validation=False, ca_certs=None,
                  trace_requests='', name=None, http_timeout=None,
-                 proxy_url=None):
+                 proxy_url=None, follow_redirects=True):
         self.auth_provider = auth_provider
         self.service = service
         self.region = region
@@ -107,11 +108,11 @@
             self.http_obj = http.ClosingProxyHttp(
                 proxy_url,
                 disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
-                timeout=http_timeout)
+                timeout=http_timeout, follow_redirects=follow_redirects)
         else:
             self.http_obj = http.ClosingHttp(
                 disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
-                timeout=http_timeout)
+                timeout=http_timeout, follow_redirects=follow_redirects)
 
     def get_headers(self, accept_type=None, send_type=None):
         """Return the default headers which will be used with outgoing requests
diff --git a/tempest/tests/lib/common/test_http.py b/tempest/tests/lib/common/test_http.py
index 02436e0..336ef4a 100644
--- a/tempest/tests/lib/common/test_http.py
+++ b/tempest/tests/lib/common/test_http.py
@@ -167,3 +167,30 @@
                          '%s://%s:%i' % (proxy.scheme,
                                          proxy.host,
                                          proxy.port))
+
+
+class TestClosingHttpRedirects(base.TestCase):
+    def setUp(self):
+        super(TestClosingHttpRedirects, self).setUp()
+
+    def test_redirect_default(self):
+        connection = http.ClosingHttp()
+        self.assertTrue(connection.follow_redirects)
+
+    def test_redirect_off(self):
+        connection = http.ClosingHttp(follow_redirects=False)
+        self.assertFalse(connection.follow_redirects)
+
+
+class TestClosingProxyHttpRedirects(base.TestCase):
+    def setUp(self):
+        super(TestClosingProxyHttpRedirects, self).setUp()
+
+    def test_redirect_default(self):
+        connection = http.ClosingProxyHttp(proxy_url=PROXY_URL)
+        self.assertTrue(connection.follow_redirects)
+
+    def test_redirect_off(self):
+        connection = http.ClosingProxyHttp(follow_redirects=False,
+                                           proxy_url=PROXY_URL)
+        self.assertFalse(connection.follow_redirects)