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
This commit is contained in:
Matt Riedemann 2018-02-05 16:07:28 -05:00
parent d8079c2e52
commit 62ef6cfcf0
4 changed files with 50 additions and 10 deletions

@ -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)

@ -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()

@ -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))

@ -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.