diff options
-rw-r--r-- | .zuul.yaml | 62 | ||||
-rw-r--r-- | README.rst | 2 | ||||
-rw-r--r-- | glance/common/wsgi.py | 31 | ||||
-rw-r--r-- | glance/domain/__init__.py | 8 | ||||
-rw-r--r-- | glance/tests/functional/__init__.py | 4 | ||||
-rw-r--r-- | glance/tests/functional/v2/test_images.py | 31 | ||||
-rw-r--r-- | glance/tests/unit/api/test_common.py | 2 | ||||
-rw-r--r-- | glance/tests/unit/common/test_utils.py | 2 | ||||
-rw-r--r-- | glance/tests/unit/common/test_wsgi.py | 62 | ||||
-rw-r--r-- | glance/tests/utils.py | 25 | ||||
-rw-r--r-- | lower-constraints.txt | 2 | ||||
-rw-r--r-- | releasenotes/notes/use-webob-1.8.1-5c3cd1b1382f063e.yaml | 12 | ||||
-rw-r--r-- | releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po | 21 | ||||
-rw-r--r-- | requirements.txt | 4 | ||||
-rw-r--r-- | tox.ini | 14 |
15 files changed, 181 insertions, 101 deletions
diff --git a/.zuul.yaml b/.zuul.yaml index 8ec164acd..5d4d325c2 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -26,6 +26,7 @@ - job: name: glance-tox-oslo-tips-base parent: tox + abstract: true description: Abstract job for Glance vs. oslo libraries # NOTE(rosmaita): only need functional test jobs, oslo is # already running periodic jobs using our unit tests @@ -65,6 +66,7 @@ - job: name: glance-tox-keystone-tips-base parent: tox + abstract: true description: Abstract job for Glance vs. keystone required-projects: - name: openstack/keystoneauth @@ -106,6 +108,7 @@ - job: name: glance-tox-glance_store-tips-base parent: tox + abstract: true description: Abstract job for Glance vs. glance_store required-projects: - name: openstack/glance_store @@ -145,6 +148,7 @@ - job: name: glance-tox-cursive-tips-base parent: tox + abstract: true description: Abstract job for Glance vs. cursive and related libs required-projects: - name: openstack/cursive @@ -265,7 +269,7 @@ - ^releasenotes/.*$ - ^setup.cfg$ - ^tox.ini$ - - legacy-tempest-dsvm-neutron-pg-full: + - tempest-pg-full: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ @@ -274,7 +278,7 @@ - ^releasenotes/.*$ - ^setup.cfg$ - ^tox.ini$ - - legacy-tempest-dsvm-neutron-full-opensuse-423: + - tempest-full-py3-opensuse150: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ @@ -285,17 +289,43 @@ - ^tox.ini$ periodic: jobs: - - glance-tox-functional-oslo-tips - - glance-tox-functional-py35-oslo-tips - - glance-tox-py27-keystone-tips - - glance-tox-py35-keystone-tips - - glance-tox-functional-keystone-tips - - glance-tox-functional-py35-keystone-tips - - glance-tox-py27-glance_store-tips - - glance-tox-py35-glance_store-tips - - glance-tox-functional-glance_store-tips - - glance-tox-functional-py35-glance_store-tips - - glance-tox-py27-cursive-tips - - glance-tox-py35-cursive-tips - - glance-tox-functional-cursive-tips - - glance-tox-functional-py35-cursive-tips + # NOTE(rosmaita): we only want the "tips" jobs to be run against + # master, hence the 'branches' qualifiers below. Without them, when + # a stable branch is cut, the tests would be run against the stable + # branch as well, which is pointless because these libraries are + # frozen (more or less) in the stable branches. + # + # The "tips" jobs can be removed from the stable branch .zuul.yaml + # files if someone is so inclined, but that would require manual + # maintenance, so we do not do it by default. Another option is + # to define these jobs in the openstack-infra/project-config repo. + # That would make us less agile in adjusting these tests, so we + # aren't doing that either. + - glance-tox-functional-oslo-tips: + branches: master + - glance-tox-functional-py35-oslo-tips: + branches: master + - glance-tox-py27-keystone-tips: + branches: master + - glance-tox-py35-keystone-tips: + branches: master + - glance-tox-functional-keystone-tips: + branches: master + - glance-tox-functional-py35-keystone-tips: + branches: master + - glance-tox-py27-glance_store-tips: + branches: master + - glance-tox-py35-glance_store-tips: + branches: master + - glance-tox-functional-glance_store-tips: + branches: master + - glance-tox-functional-py35-glance_store-tips: + branches: master + - glance-tox-py27-cursive-tips: + branches: master + - glance-tox-py35-cursive-tips: + branches: master + - glance-tox-functional-cursive-tips: + branches: master + - glance-tox-functional-py35-cursive-tips: + branches: master diff --git a/README.rst b/README.rst index 7c9bc115a..c267df725 100644 --- a/README.rst +++ b/README.rst @@ -71,7 +71,7 @@ Release notes To learn more about Glance's new features, optimizations, and changes between versions, consult the release notes online at: -* `Release Notes <https://docs.openstack.org/releasenotes/glance/>`_ +* `Release Notes <https://docs.openstack.org/releasenotes/glance/>`__ Other Information ----------------- diff --git a/glance/common/wsgi.py b/glance/common/wsgi.py index bc0bb084f..7ccd76a2f 100644 --- a/glance/common/wsgi.py +++ b/glance/common/wsgi.py @@ -53,12 +53,6 @@ from glance import i18n from glance.i18n import _, _LE, _LI, _LW -try: - from webob.acceptparse import AcceptLanguageValidHeader # noqa - USING_WEBOB_1_8 = True -except ImportError: - USING_WEBOB_1_8 = False - bind_opts = [ cfg.HostAddressOpt('bind_host', default='0.0.0.0', @@ -1033,8 +1027,10 @@ class Request(webob.Request): def best_match_content_type(self): """Determine the requested response content-type.""" supported = ('application/json',) - bm = self.accept.best_match(supported) - return bm or 'application/json' + best_matches = self.accept.acceptable_offers(supported) + if not best_matches: + return 'application/json' + return best_matches[0][0] def get_content_type(self, allowed_content_types): """Determine content type of the request body.""" @@ -1048,18 +1044,7 @@ class Request(webob.Request): else: return content_type - def _best_match_language_1_7(self): - """Determines best available locale from the Accept-Language header. - - :returns: the best language match or None if the 'Accept-Language' - header was not available in the request. - """ - if not self.accept_language: - return None - langs = i18n.get_available_languages('glance') - return self.accept_language.best_match(langs) - - def _best_match_language_1_8(self): + def best_match_language(self): """Determines best available locale from the Accept-Language header. :returns: the best language match or None if the 'Accept-Language' @@ -1076,12 +1061,6 @@ class Request(webob.Request): best_match = None return best_match - def best_match_language(self): - if USING_WEBOB_1_8: - return self._best_match_language_1_8() - else: - return self._best_match_language_1_7() - def get_range_from_request(self, image_size): """Return the `Range` in a request.""" diff --git a/glance/domain/__init__.py b/glance/domain/__init__.py index 7feb1edfb..0bb6a2964 100644 --- a/glance/domain/__init__.py +++ b/glance/domain/__init__.py @@ -300,9 +300,9 @@ class ExtraProperties(collections.MutableMapping, dict): def __eq__(self, other): if isinstance(other, ExtraProperties): - return dict(self).__eq__(dict(other)) + return dict.__eq__(self, dict(other)) elif isinstance(other, dict): - return dict(self).__eq__(other) + return dict.__eq__(self, other) else: return False @@ -310,10 +310,10 @@ class ExtraProperties(collections.MutableMapping, dict): return not self.__eq__(other) def __len__(self): - return dict(self).__len__() + return dict.__len__(self) def keys(self): - return dict(self).keys() + return dict.keys(self) class ImageMembership(object): diff --git a/glance/tests/functional/__init__.py b/glance/tests/functional/__init__.py index d6ac835dd..ac42b1da2 100644 --- a/glance/tests/functional/__init__.py +++ b/glance/tests/functional/__init__.py @@ -364,6 +364,8 @@ store_type_preference = %(store_type_location_strategy_preference)s [glance_store] filesystem_store_datadir=%(image_dir)s default_store = %(default_store)s +[import_filtering_opts] +allowed_ports = [] """ self.paste_conf_base = """[pipeline:glance-api] pipeline = @@ -549,6 +551,8 @@ default_backend = %(default_backend)s filesystem_store_datadir=%(image_dir_backend_1)s [file2] filesystem_store_datadir=%(image_dir_backend_2)s +[import_filtering_opts] +allowed_ports = [] """ self.paste_conf_base = """[pipeline:glance-api] pipeline = diff --git a/glance/tests/functional/v2/test_images.py b/glance/tests/functional/v2/test_images.py index eb584365b..7d6d93398 100644 --- a/glance/tests/functional/v2/test_images.py +++ b/glance/tests/functional/v2/test_images.py @@ -341,8 +341,11 @@ class TestImages(functional.FunctionalTest): 'content-type': 'application/json', 'X-Roles': 'admin', }) - image_data_uri = ('https://www.openstack.org/assets/openstack-logo/' - '2016R/OpenStack-Logo-Horizontal.eps.zip') + + # Start http server locally + pid, port = test_utils.start_standalone_http_server() + + image_data_uri = 'http://localhost:%s/' % port data = jsonutils.dumps({'method': { 'name': 'web-download', 'uri': image_data_uri @@ -369,6 +372,9 @@ class TestImages(functional.FunctionalTest): os_hash_value=expect_h, status='active') + # kill the local http server + os.kill(pid, signal.SIGKILL) + # Deleting image should work path = self._url('/v2/images/%s' % image_id) response = requests.delete(path, headers=self._headers()) @@ -4901,8 +4907,11 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest): 'content-type': 'application/json', 'X-Roles': 'admin', }) - image_data_uri = ('https://www.openstack.org/assets/openstack-logo/' - '2016R/OpenStack-Logo-Horizontal.eps.zip') + + # Start http server locally + pid, port = test_utils.start_standalone_http_server() + + image_data_uri = 'http://localhost:%s/' % port data = jsonutils.dumps({'method': { 'name': 'web-download', 'uri': image_data_uri @@ -4928,6 +4937,9 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest): checksum=expect_c, os_hash_value=expect_h, status='active') + + # kill the local http server + os.kill(pid, signal.SIGKILL) # Ensure image is created in default backend path = self._url('/v2/images/%s' % image_id) response = requests.get(path, headers=self._headers()) @@ -5055,8 +5067,11 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest): 'X-Roles': 'admin', 'X-Image-Meta-Store': 'file2' }) - image_data_uri = ('https://www.openstack.org/assets/openstack-logo/' - '2016R/OpenStack-Logo-Horizontal.eps.zip') + + # Start http server locally + pid, port = test_utils.start_standalone_http_server() + + image_data_uri = 'http://localhost:%s/' % port data = jsonutils.dumps({'method': { 'name': 'web-download', 'uri': image_data_uri @@ -5082,6 +5097,10 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest): checksum=expect_c, os_hash_value=expect_h, status='active') + + # kill the local http server + os.kill(pid, signal.SIGKILL) + # Ensure image is created in different backend path = self._url('/v2/images/%s' % image_id) response = requests.get(path, headers=self._headers()) diff --git a/glance/tests/unit/api/test_common.py b/glance/tests/unit/api/test_common.py index ab1ccb995..cf3c9f93f 100644 --- a/glance/tests/unit/api/test_common.py +++ b/glance/tests/unit/api/test_common.py @@ -34,7 +34,7 @@ class SimpleIterator(object): yield chunk chunk = read_chunk() else: - raise StopIteration() + return class TestSizeCheckedIter(testtools.TestCase): diff --git a/glance/tests/unit/common/test_utils.py b/glance/tests/unit/common/test_utils.py index 48911c673..98ff61c21 100644 --- a/glance/tests/unit/common/test_utils.py +++ b/glance/tests/unit/common/test_utils.py @@ -87,7 +87,7 @@ class TestUtils(test_utils.BaseTestCase): yield chunk iteration += 1 if iteration >= max_iterations: - raise StopIteration() + return def _test_reader_chunked(self, chunk_size, read_size, max_iterations=5): generator = self._create_generator(chunk_size, max_iterations) diff --git a/glance/tests/unit/common/test_wsgi.py b/glance/tests/unit/common/test_wsgi.py index adda23e11..794873d42 100644 --- a/glance/tests/unit/common/test_wsgi.py +++ b/glance/tests/unit/common/test_wsgi.py @@ -184,31 +184,20 @@ class RequestTest(test_utils.BaseTestCase): req = wsgi.Request.blank('/', headers={'Accept-Language': 'unknown'}) self.assertIsNone(req.best_match_language()) - def test_best_match_language_unknown(self): - # Test that we are actually invoking language negotiation by webop + @mock.patch.object(webob.acceptparse.AcceptLanguageValidHeader, 'lookup') + def test_best_match_language_unknown(self, mock_lookup): + # Test that we are actually invoking language negotiation by WebOb request = wsgi.Request.blank('/') accepted = 'unknown-lang' request.headers = {'Accept-Language': accepted} - # TODO(rosmaita): simplify when lower_constraints has webob >= 1.8.1 - try: - from webob.acceptparse import AcceptLanguageValidHeader # noqa - cls = webob.acceptparse.AcceptLanguageValidHeader - funcname = 'lookup' - # Bug #1765748: see comment in code in the function under test - # to understand why this is the correct return value for the - # webob 1.8.x mock - retval = 'fake_LANG' - except ImportError: - cls = webob.acceptparse.AcceptLanguage - funcname = 'best_match' - retval = None - - with mock.patch.object(cls, funcname) as mocked_function: - mocked_function.return_value = retval - - self.assertIsNone(request.best_match_language()) - mocked_function.assert_called_once() + # Bug #1765748: see comment in code in the function under test + # to understand why this is the correct return value for the + # webob 1.8.x mock + mock_lookup.return_value = 'fake_LANG' + + self.assertIsNone(request.best_match_language()) + mock_lookup.assert_called_once() # If Accept-Language is missing or empty, match should be None request.headers = {'Accept-Language': ''} @@ -389,27 +378,18 @@ class ResourceTest(test_utils.BaseTestCase): resource, request) self.assertEqual(message_es, str(e)) + @mock.patch.object(webob.acceptparse.AcceptLanguageValidHeader, 'lookup') @mock.patch.object(i18n, 'translate') - def test_translate_exception(self, mock_translate): - # TODO(rosmaita): simplify when lower_constraints has webob >= 1.8.1 - try: - from webob.acceptparse import AcceptLanguageValidHeader # noqa - cls = webob.acceptparse.AcceptLanguageValidHeader - funcname = 'lookup' - except ImportError: - cls = webob.acceptparse.AcceptLanguage - funcname = 'best_match' - - with mock.patch.object(cls, funcname) as mocked_function: - mock_translate.return_value = 'No Encontrado' - mocked_function.return_value = 'de' - - req = wsgi.Request.blank('/tests/123') - req.headers["Accept-Language"] = "de" - - e = webob.exc.HTTPNotFound(explanation='Not Found') - e = wsgi.translate_exception(req, e) - self.assertEqual('No Encontrado', e.explanation) + def test_translate_exception(self, mock_translate, mock_lookup): + mock_translate.return_value = 'No Encontrado' + mock_lookup.return_value = 'de' + + req = wsgi.Request.blank('/tests/123') + req.headers["Accept-Language"] = "de" + + e = webob.exc.HTTPNotFound(explanation='Not Found') + e = wsgi.translate_exception(req, e) + self.assertEqual('No Encontrado', e.explanation) def test_response_headers_encoded(self): # prepare environment diff --git a/glance/tests/utils.py b/glance/tests/utils.py index 0b0d143b0..bc55f5b0f 100644 --- a/glance/tests/utils.py +++ b/glance/tests/utils.py @@ -713,3 +713,28 @@ def is_sqlite_version_prior_to(major, minor): import sqlite3 tup = sqlite3.sqlite_version_info return tup[0] < major or (tup[0] == major and tup[1] < minor) + + +def start_standalone_http_server(): + def _get_http_handler_class(): + class StaticHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(self): + data = b"Hello World!!!" + self.send_response(http.OK) + self.send_header('Content-Length', str(len(data))) + self.end_headers() + self.wfile.write(data) + return + + return StaticHTTPRequestHandler + + server_address = ('127.0.0.1', 0) + handler_class = _get_http_handler_class() + httpd = BaseHTTPServer.HTTPServer(server_address, handler_class) + port = httpd.socket.getsockname()[1] + + pid = os.fork() + if pid == 0: + httpd.serve_forever() + else: + return pid, port diff --git a/lower-constraints.txt b/lower-constraints.txt index beac1f160..4e158cd56 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -138,7 +138,7 @@ unittest2==1.1.0 urllib3==1.22 vine==1.1.4 voluptuous==0.11.1 -WebOb==1.7.1 +WebOb==1.8.1 whereto===0.3.0 wrapt==1.10.11 WSME==0.8.0 diff --git a/releasenotes/notes/use-webob-1.8.1-5c3cd1b1382f063e.yaml b/releasenotes/notes/use-webob-1.8.1-5c3cd1b1382f063e.yaml new file mode 100644 index 000000000..21d8d3e5b --- /dev/null +++ b/releasenotes/notes/use-webob-1.8.1-5c3cd1b1382f063e.yaml @@ -0,0 +1,12 @@ +--- +other: + - | + Negotiation of the 'Accept-Language' header now follows the "Lookup" + matching scheme described in `RFC 4647, section 3.4 + <https://tools.ietf.org/html/rfc4647.html#section-3.4>`_. The + "Lookup" scheme is one of the algorithms suggested in `RFC 7231, + section 5.3.5 + <https://tools.ietf.org/html/rfc7231.html#section-5.3.5>`_. (This is + due to a change in an underlying library, which previously used a + matching scheme that did not conform to `RFC 7231 + <https://tools.ietf.org/html/rfc7231.html>`_.) diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po index 63c1020c4..801bdcdbf 100644 --- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -4,11 +4,11 @@ msgid "" msgstr "" "Project-Id-Version: Glance Release Notes\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-09-21 23:35+0000\n" +"POT-Creation-Date: 2018-11-08 05:54+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-09-27 12:04+0000\n" +"PO-Revision-Date: 2018-11-07 06:12+0000\n" "Last-Translator: Andi Chandler <andi@gowling.com>\n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" @@ -1274,6 +1274,23 @@ msgstr "" msgid "Mitaka Series Release Notes" msgstr "Mitaka Series Release Notes" +msgid "" +"Negotiation of the 'Accept-Language' header now follows the \"Lookup\" " +"matching scheme described in `RFC 4647, section 3.4 <https://tools.ietf.org/" +"html/rfc4647.html#section-3.4>`_. The \"Lookup\" scheme is one of the " +"algorithms suggested in `RFC 7231, section 5.3.5 <https://tools.ietf.org/" +"html/rfc7231.html#section-5.3.5>`_. (This is due to a change in an " +"underlying library, which previously used a matching scheme that did not " +"conform to `RFC 7231 <https://tools.ietf.org/html/rfc7231.html>`_.)" +msgstr "" +"Negotiation of the 'Accept-Language' header now follows the \"Lookup\" " +"matching scheme described in `RFC 4647, section 3.4 <https://tools.ietf.org/" +"html/rfc4647.html#section-3.4>`_. The \"Lookup\" scheme is one of the " +"algorithms suggested in `RFC 7231, section 5.3.5 <https://tools.ietf.org/" +"html/rfc7231.html#section-5.3.5>`_. (This is due to a change in an " +"underlying library, which previously used a matching scheme that did not " +"conform to `RFC 7231 <https://tools.ietf.org/html/rfc7231.html>`_.)" + msgid "New Features" msgstr "New Features" diff --git a/requirements.txt b/requirements.txt index 1b5027124..ee25e7e79 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT PasteDeploy>=1.5.0 # MIT Routes>=2.3.1 # MIT -WebOb>=1.7.1 # MIT +WebOb>=1.8.1 # MIT sqlalchemy-migrate>=0.11.0 # Apache-2.0 sqlparse>=0.2.2 # BSD alembic>=0.8.10 # MIT @@ -38,7 +38,7 @@ six>=1.10.0 # MIT oslo.db>=4.27.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 -oslo.messaging>=5.29.0 # Apache-2.0 +oslo.messaging>=5.29.0,!=9.0.0 # Apache-2.0 oslo.middleware>=3.31.0 # Apache-2.0 oslo.policy>=1.30.0 # Apache-2.0 @@ -34,6 +34,10 @@ commands = ostestr --slowest {posargs} basepython = python3.5 commands = ostestr --slowest {posargs} +[testenv:py37] +basepython = python3.7 +commands = ostestr --slowest {posargs} + [testenv:functional] setenv = TEST_PATH = ./glance/tests/functional @@ -51,6 +55,16 @@ whitelist_externals = commands = stestr run --blacklist-file ./broken-functional-py35-ssl-tests.txt {posargs} +[testenv:functional-py37] +basepython = python3.7 +setenv = + TEST_PATH = ./glance/tests/functional +ignore_errors = True +whitelist_externals = + bash +commands = + stestr run --blacklist-file ./broken-functional-py35-ssl-tests.txt {posargs} + [testenv:broken-py35-ssl-tests] # NOTE(rosmaita): these tests were being skipped due to bug #1482633, but we # want it to be obvious that Glance is affected by the eventlet ssl-handshake |