From 62ef6cfcf01d84813f71d1e8252b86c170ee39f0 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Mon, 5 Feb 2018 16:07:28 -0500 Subject: [PATCH] Workaround glanceclient bug when CONF.glance.api_servers not set In certain configurations, like when setting [service_user] config, and not setting [glance]/api_servers, the KSA adapter get endpoint code (new in Queens) will return a versioned URL which glanceclient doesn't handle (due to bug 1707995) so we need to workaround that by parsing the URL to strip the version from the endpoint URL we got from KSA. This is validated in the nova-next CI job which configures a service user token for glance. Change-Id: I363182e916480c734cc37f279e8e89c8f3ec653c Closes-Bug: #1747511 Related-Bug: #1707995 --- nova/image/glance.py | 10 ++++- nova/tests/unit/compute/test_compute_mgr.py | 1 + nova/tests/unit/image/test_glance.py | 38 ++++++++++++++----- ...1-glance-api-servers-1e17757b901a76d8.yaml | 11 ++++++ 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/bug-1747511-glance-api-servers-1e17757b901a76d8.yaml diff --git a/nova/image/glance.py b/nova/image/glance.py index 2de3228fb0..1a8d532afb 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -22,6 +22,7 @@ import inspect import itertools import os import random +import re import stat import sys import time @@ -122,7 +123,14 @@ def get_api_servers(context): nova.conf.glance.DEFAULT_SERVICE_TYPE, ksa_auth=auth, ksa_session=sess, min_version='2.0', max_version='2.latest') - api_servers = [utils.get_endpoint(ksa_adap)] + endpoint = utils.get_endpoint(ksa_adap) + if endpoint: + # NOTE(mriedem): Due to python-glanceclient bug 1707995 we have + # to massage the endpoint URL otherwise it won't work properly. + # We can't use glanceclient.common.utils.strip_version because + # of bug 1748009. + endpoint = re.sub(r'/v\d+(\.\d+)?/?$', '/', endpoint) + api_servers = [endpoint] return itertools.cycle(api_servers) diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index b511a00ef6..e775e67aad 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -3960,6 +3960,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): mock_setup_networks, mock_setup_intance_network, mock_get_bdms, mock_mutate_migration, mock_appy_migration, mock_drop_migration, mock_context_elevated): + self.flags(api_servers=['http://localhost/image/v2'], group='glance') instance = fake_instance.fake_instance_obj(self.context) instance.info_cache = None elevated_context = mock.Mock() diff --git a/nova/tests/unit/image/test_glance.py b/nova/tests/unit/image/test_glance.py index bae2fbc356..d1507827b4 100644 --- a/nova/tests/unit/image/test_glance.py +++ b/nova/tests/unit/image/test_glance.py @@ -19,6 +19,7 @@ import datetime import cryptography from cursive import exception as cursive_exception +import ddt import glanceclient.exc from glanceclient.v1 import images import glanceclient.v2.schemas as schemas @@ -1601,6 +1602,7 @@ class TestDelete(test.NoDBTestCase): mock.sentinel.image_id) +@ddt.ddt class TestGlanceApiServers(test.NoDBTestCase): def test_get_api_servers_multiple(self): @@ -1615,18 +1617,36 @@ class TestGlanceApiServers(test.NoDBTestCase): self.assertEqual(expected_servers, {next(api_servers) for _ in expected_servers}) - @mock.patch('keystoneauth1.adapter.Adapter.get_endpoint_data') - def test_get_api_servers_get_ksa_adapter(self, mock_epd): + @ddt.data(['http://158.69.92.100/image/v2/', + 'http://158.69.92.100/image/'], + ['http://158.69.92.100/image/v2', + 'http://158.69.92.100/image/'], + ['http://158.69.92.100/image/v2.0/', + 'http://158.69.92.100/image/'], + ['http://158.69.92.100/image/', + 'http://158.69.92.100/image/'], + ['http://158.69.92.100/image', + 'http://158.69.92.100/image'], + ['http://158.69.92.100/v2', + 'http://158.69.92.100/'], + ['http://thing.novav2.0oh.v2.foo/image/v2/', + 'http://thing.novav2.0oh.v2.foo/image/']) + @ddt.unpack + def test_get_api_servers_get_ksa_adapter(self, catalog_url, stripped): """Test get_api_servers via nova.utils.get_ksa_adapter().""" self.flags(api_servers=None, group='glance') - api_servers = glance.get_api_servers(mock.Mock()) - self.assertEqual(mock_epd.return_value.catalog_url, next(api_servers)) - # Still get itertools.cycle behavior - self.assertEqual(mock_epd.return_value.catalog_url, next(api_servers)) - mock_epd.assert_called_once_with() + with mock.patch('keystoneauth1.adapter.Adapter.' + 'get_endpoint_data') as mock_epd: + mock_epd.return_value.catalog_url = catalog_url + api_servers = glance.get_api_servers(mock.Mock()) + self.assertEqual(stripped, next(api_servers)) + # Still get itertools.cycle behavior + self.assertEqual(stripped, next(api_servers)) + mock_epd.assert_called_once_with() - # Now test with endpoint_override - get_endpoint_data is not called. - mock_epd.reset_mock() + @mock.patch('keystoneauth1.adapter.Adapter.get_endpoint_data') + def test_get_api_servers_get_ksa_adapter_endpoint_override(self, + mock_epd): self.flags(endpoint_override='foo', group='glance') api_servers = glance.get_api_servers(mock.Mock()) self.assertEqual('foo', next(api_servers)) diff --git a/releasenotes/notes/bug-1747511-glance-api-servers-1e17757b901a76d8.yaml b/releasenotes/notes/bug-1747511-glance-api-servers-1e17757b901a76d8.yaml new file mode 100644 index 0000000000..e81a40d6c9 --- /dev/null +++ b/releasenotes/notes/bug-1747511-glance-api-servers-1e17757b901a76d8.yaml @@ -0,0 +1,11 @@ +--- +issues: + - | + Due to a bug in python-glanceclient: + + https://bugs.launchpad.net/python-glanceclient/+bug/1707995 + + If ``[glance]/api_servers`` is not set in nova.conf, and there is a + versioned endpoint URL in the service catalog, nova makes a best attempt + at parsing and stripping the version from the URL in order to make + API requests to the image service.