summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oslo/__init__.py13
-rw-r--r--oslo/middleware/__init__.py28
-rw-r--r--oslo/middleware/base.py13
-rw-r--r--oslo/middleware/catch_errors.py13
-rw-r--r--oslo/middleware/correlation_id.py13
-rw-r--r--oslo/middleware/debug.py13
-rw-r--r--oslo/middleware/request_id.py13
-rw-r--r--oslo/middleware/sizelimit.py13
-rw-r--r--setup.cfg4
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/test_catch_errors.py47
-rw-r--r--tests/test_correlation_id.py53
-rw-r--r--tests/test_request_id.py37
-rw-r--r--tests/test_sizelimit.py108
-rw-r--r--tests/test_warning.py61
15 files changed, 429 insertions, 0 deletions
diff --git a/oslo/__init__.py b/oslo/__init__.py
new file mode 100644
index 0000000..dc130d6
--- /dev/null
+++ b/oslo/__init__.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/oslo/middleware/__init__.py b/oslo/middleware/__init__.py
new file mode 100644
index 0000000..1407ce1
--- /dev/null
+++ b/oslo/middleware/__init__.py
@@ -0,0 +1,28 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import warnings
+
+from oslo_middleware import *
+
+
+def deprecated():
+ new_name = __name__.replace('.', '_')
+ warnings.warn(
+ ('The oslo namespace package is deprecated. Please use %s instead.' %
+ new_name),
+ DeprecationWarning,
+ stacklevel=3,
+ )
+
+
+deprecated()
diff --git a/oslo/middleware/base.py b/oslo/middleware/base.py
new file mode 100644
index 0000000..53e25e9
--- /dev/null
+++ b/oslo/middleware/base.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_middleware.base import * # noqa
diff --git a/oslo/middleware/catch_errors.py b/oslo/middleware/catch_errors.py
new file mode 100644
index 0000000..81e4c6c
--- /dev/null
+++ b/oslo/middleware/catch_errors.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_middleware.catch_errors import * # noqa
diff --git a/oslo/middleware/correlation_id.py b/oslo/middleware/correlation_id.py
new file mode 100644
index 0000000..fff548c
--- /dev/null
+++ b/oslo/middleware/correlation_id.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_middleware.correlation_id import * # noqa
diff --git a/oslo/middleware/debug.py b/oslo/middleware/debug.py
new file mode 100644
index 0000000..2907289
--- /dev/null
+++ b/oslo/middleware/debug.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_middleware.debug import * # noqa
diff --git a/oslo/middleware/request_id.py b/oslo/middleware/request_id.py
new file mode 100644
index 0000000..81e3164
--- /dev/null
+++ b/oslo/middleware/request_id.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_middleware.request_id import * # noqa
diff --git a/oslo/middleware/sizelimit.py b/oslo/middleware/sizelimit.py
new file mode 100644
index 0000000..c04c1cd
--- /dev/null
+++ b/oslo/middleware/sizelimit.py
@@ -0,0 +1,13 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_middleware.sizelimit import * # noqa
diff --git a/setup.cfg b/setup.cfg
index e609b9a..71103fe 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -21,7 +21,11 @@ classifier =
[files]
packages =
+ oslo
+ oslo.middleware
oslo_middleware
+namespace_packages =
+ oslo
[entry_points]
oslo.config.opts =
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/test_catch_errors.py b/tests/test_catch_errors.py
new file mode 100644
index 0000000..9e71f5b
--- /dev/null
+++ b/tests/test_catch_errors.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+from oslotest import base as test_base
+import webob.dec
+import webob.exc
+
+from oslo.middleware import catch_errors
+
+
+class CatchErrorsTest(test_base.BaseTestCase):
+
+ def _test_has_request_id(self, application, expected_code=None):
+ app = catch_errors.CatchErrors(application)
+ req = webob.Request.blank('/test')
+ res = req.get_response(app)
+ self.assertEqual(expected_code, res.status_int)
+
+ def test_success_response(self):
+ @webob.dec.wsgify
+ def application(req):
+ return 'Hello, World!!!'
+
+ self._test_has_request_id(application, webob.exc.HTTPOk.code)
+
+ def test_internal_server_error(self):
+ @webob.dec.wsgify
+ def application(req):
+ raise Exception()
+
+ with mock.patch.object(catch_errors.LOG, 'exception') as log_exc:
+ self._test_has_request_id(application,
+ webob.exc.HTTPInternalServerError.code)
+ self.assertEqual(1, log_exc.call_count)
diff --git a/tests/test_correlation_id.py b/tests/test_correlation_id.py
new file mode 100644
index 0000000..b927e1d
--- /dev/null
+++ b/tests/test_correlation_id.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013 Rackspace Hosting
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+import mock
+from oslotest import base as test_base
+from oslotest import moxstubout
+
+from oslo.middleware import correlation_id
+
+
+class CorrelationIdTest(test_base.BaseTestCase):
+
+ def setUp(self):
+ super(CorrelationIdTest, self).setUp()
+ self.stubs = self.useFixture(moxstubout.MoxStubout()).stubs
+
+ def test_process_request(self):
+ app = mock.Mock()
+ req = mock.Mock()
+ req.headers = {}
+
+ mock_uuid4 = mock.Mock()
+ mock_uuid4.return_value = "fake_uuid"
+ self.stubs.Set(uuid, 'uuid4', mock_uuid4)
+
+ middleware = correlation_id.CorrelationId(app)
+ middleware(req)
+
+ self.assertEqual(req.headers.get("X_CORRELATION_ID"), "fake_uuid")
+
+ def test_process_request_should_not_regenerate_correlation_id(self):
+ app = mock.Mock()
+ req = mock.Mock()
+ req.headers = {"X_CORRELATION_ID": "correlation_id"}
+
+ middleware = correlation_id.CorrelationId(app)
+ middleware(req)
+
+ self.assertEqual(req.headers.get("X_CORRELATION_ID"), "correlation_id")
diff --git a/tests/test_request_id.py b/tests/test_request_id.py
new file mode 100644
index 0000000..549d7be
--- /dev/null
+++ b/tests/test_request_id.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from oslotest import base as test_base
+from testtools import matchers
+import webob
+import webob.dec
+
+from oslo.middleware import request_id
+
+
+class RequestIdTest(test_base.BaseTestCase):
+ def test_generate_request_id(self):
+ @webob.dec.wsgify
+ def application(req):
+ return req.environ[request_id.ENV_REQUEST_ID]
+
+ app = request_id.RequestId(application)
+ req = webob.Request.blank('/test')
+ res = req.get_response(app)
+ res_req_id = res.headers.get(request_id.HTTP_RESP_HEADER_REQUEST_ID)
+ self.assertThat(res_req_id, matchers.StartsWith(b'req-'))
+ # request-id in request environ is returned as response body
+ self.assertEqual(res_req_id, res.body)
diff --git a/tests/test_sizelimit.py b/tests/test_sizelimit.py
new file mode 100644
index 0000000..0f26a49
--- /dev/null
+++ b/tests/test_sizelimit.py
@@ -0,0 +1,108 @@
+# Copyright (c) 2012 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslotest import base as test_base
+import six
+import webob
+
+from oslo.config import fixture as config
+from oslo.middleware import sizelimit
+
+
+class TestLimitingReader(test_base.BaseTestCase):
+
+ def test_limiting_reader(self):
+ BYTES = 1024
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ for chunk in sizelimit.LimitingReader(data, BYTES):
+ bytes_read += len(chunk)
+
+ self.assertEqual(bytes_read, BYTES)
+
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ reader = sizelimit.LimitingReader(data, BYTES)
+ byte = reader.read(1)
+ while len(byte) != 0:
+ bytes_read += 1
+ byte = reader.read(1)
+
+ self.assertEqual(bytes_read, BYTES)
+
+ def test_read_default_value(self):
+ BYTES = 1024
+ data_str = "*" * BYTES
+ data = six.StringIO(data_str)
+ reader = sizelimit.LimitingReader(data, BYTES)
+ res = reader.read()
+ self.assertEqual(data_str, res)
+
+ def test_limiting_reader_fails(self):
+ BYTES = 1024
+
+ def _consume_all_iter():
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ for chunk in sizelimit.LimitingReader(data, BYTES - 1):
+ bytes_read += len(chunk)
+
+ self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
+ _consume_all_iter)
+
+ def _consume_all_read():
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ reader = sizelimit.LimitingReader(data, BYTES - 1)
+ byte = reader.read(1)
+ while len(byte) != 0:
+ bytes_read += 1
+ byte = reader.read(1)
+
+ self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
+ _consume_all_read)
+
+
+class TestRequestBodySizeLimiter(test_base.BaseTestCase):
+
+ def setUp(self):
+ super(TestRequestBodySizeLimiter, self).setUp()
+ fixture = self.useFixture(config.Config(sizelimit.CONF))
+ self.MAX_REQUEST_BODY_SIZE = \
+ fixture.conf.oslo_middleware.max_request_body_size
+
+ @webob.dec.wsgify()
+ def fake_app(req):
+ return webob.Response(req.body)
+
+ self.middleware = sizelimit.RequestBodySizeLimiter(fake_app)
+ self.request = webob.Request.blank('/', method='POST')
+
+ def test_content_length_acceptable(self):
+ self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE
+ self.request.body = b"0" * self.MAX_REQUEST_BODY_SIZE
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(response.status_int, 200)
+
+ def test_content_length_too_large(self):
+ self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE + 1
+ self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(response.status_int, 413)
+
+ def test_request_too_large_no_content_length(self):
+ self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
+ self.request.headers['Content-Length'] = None
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(response.status_int, 413)
diff --git a/tests/test_warning.py b/tests/test_warning.py
new file mode 100644
index 0000000..8e7d96c
--- /dev/null
+++ b/tests/test_warning.py
@@ -0,0 +1,61 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import imp
+import os
+import warnings
+
+import mock
+from oslotest import base as test_base
+import six
+
+
+class DeprecationWarningTest(test_base.BaseTestCase):
+
+ @mock.patch('warnings.warn')
+ def test_warning(self, mock_warn):
+ import oslo.middleware
+ imp.reload(oslo.middleware)
+ self.assertTrue(mock_warn.called)
+ args = mock_warn.call_args
+ self.assertIn('oslo_middleware', args[0][0])
+ self.assertIn('deprecated', args[0][0])
+ self.assertTrue(issubclass(args[0][1], DeprecationWarning))
+
+ def test_real_warning(self):
+ with warnings.catch_warnings(record=True) as warning_msgs:
+ warnings.resetwarnings()
+ warnings.simplefilter('always', DeprecationWarning)
+ import oslo.middleware
+
+ # Use a separate function to get the stack level correct
+ # so we know the message points back to this file. This
+ # corresponds to an import or reload, which isn't working
+ # inside the test under Python 3.3. That may be due to a
+ # difference in the import implementation not triggering
+ # warnings properly when the module is reloaded, or
+ # because the warnings module is mostly implemented in C
+ # and something isn't cleanly resetting the global state
+ # used to track whether a warning needs to be
+ # emitted. Whatever the cause, we definitely see the
+ # warnings.warn() being invoked on a reload (see the test
+ # above) and warnings are reported on the console when we
+ # run the tests. A simpler test script run outside of
+ # testr does correctly report the warnings.
+ def foo():
+ oslo.middleware.deprecated()
+
+ foo()
+ self.assertEqual(1, len(warning_msgs))
+ msg = warning_msgs[0]
+ self.assertIn('oslo_middleware', six.text_type(msg.message))
+ self.assertEqual('test_warning.py', os.path.basename(msg.filename))