summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-05-08 15:40:15 +0000
committerGerrit Code Review <review@openstack.org>2018-05-08 15:40:15 +0000
commit3308cd9944eba26735ec5d66452c22204a62ea89 (patch)
tree6ecc1eb9670c96672f9f0ce51092fd9462ec5f21
parent9688b7938bc2281eecce935d586fd1ce0df21d80 (diff)
parent5c79260971a87a5af002b3eeab82866928e88ac9 (diff)
Merge "Infer version from old versioned service type aliases"3.6.0
-rw-r--r--keystoneauth1/discover.py50
-rw-r--r--keystoneauth1/exceptions/discovery.py32
-rw-r--r--keystoneauth1/identity/base.py6
-rw-r--r--keystoneauth1/tests/unit/identity/test_identity_common.py26
-rw-r--r--keystoneauth1/tests/unit/test_discovery.py61
5 files changed, 142 insertions, 33 deletions
diff --git a/keystoneauth1/discover.py b/keystoneauth1/discover.py
index 2e79a23..a3be0f9 100644
--- a/keystoneauth1/discover.py
+++ b/keystoneauth1/discover.py
@@ -220,7 +220,28 @@ def normalize_version_number(version):
220 raise TypeError('Invalid version specified: %s' % version) 220 raise TypeError('Invalid version specified: %s' % version)
221 221
222 222
223def _normalize_version_args(version, min_version, max_version): 223def _normalize_version_args(
224 version, min_version, max_version, service_type=None):
225 # The sins of our fathers become the blood on our hands.
226 # If a user requests an old-style service type such as volumev2, then they
227 # are inherently requesting the major API version 2. It's not a good
228 # interface, but it's the one that was imposed on the world years ago
229 # because the client libraries all hid the version discovery document.
230 # In order to be able to ensure that a user who requests volumev2 does not
231 # get a block-storage endpoint that only provides v3 of the block-storage
232 # service, we need to pull the version out of the service_type. The
233 # service-types-authority will prevent the growth of new monstrosities such
234 # as this, but in order to move forward without breaking people, we have
235 # to just cry in the corner while striking ourselves with thorned branches.
236 # That said, for sure only do this hack for officially known service_types.
237 if (service_type and
238 _SERVICE_TYPES.is_known(service_type) and
239 service_type[-1].isdigit() and
240 service_type[-2] == 'v'):
241 implied_version = normalize_version_number(service_type[-1])
242 else:
243 implied_version = None
244
224 if version and (min_version or max_version): 245 if version and (min_version or max_version):
225 raise ValueError( 246 raise ValueError(
226 "version is mutually exclusive with min_version and max_version") 247 "version is mutually exclusive with min_version and max_version")
@@ -229,6 +250,12 @@ def _normalize_version_args(version, min_version, max_version):
229 # Explode this into min_version and max_version 250 # Explode this into min_version and max_version
230 min_version = normalize_version_number(version) 251 min_version = normalize_version_number(version)
231 max_version = (min_version[0], LATEST) 252 max_version = (min_version[0], LATEST)
253 if implied_version:
254 if min_version[0] != implied_version[0]:
255 raise exceptions.ImpliedVersionMismatch(
256 service_type=service_type,
257 implied=implied_version,
258 given=version_to_string(version))
232 return min_version, max_version 259 return min_version, max_version
233 260
234 if min_version == 'latest': 261 if min_version == 'latest':
@@ -257,6 +284,27 @@ def _normalize_version_args(version, min_version, max_version):
257 if None not in (min_version, max_version) and max_version < min_version: 284 if None not in (min_version, max_version) and max_version < min_version:
258 raise ValueError("min_version cannot be greater than max_version") 285 raise ValueError("min_version cannot be greater than max_version")
259 286
287 if implied_version:
288 if min_version:
289 if min_version[0] != implied_version[0]:
290 raise exceptions.ImpliedMinVersionMismatch(
291 service_type=service_type,
292 implied=implied_version,
293 given=version_to_string(min_version))
294 else:
295 min_version = implied_version
296
297 # If 'latest' is provided with a versioned service-type like
298 # volumev2 - the user wants the latest of volumev2, not the latest
299 # of block-storage.
300 if max_version and max_version[0] != LATEST:
301 if max_version[0] != implied_version[0]:
302 raise exceptions.ImpliedMaxVersionMismatch(
303 service_type=service_type,
304 implied=implied_version,
305 given=version_to_string(max_version))
306 else:
307 max_version = (implied_version[0], LATEST)
260 return min_version, max_version 308 return min_version, max_version
261 309
262 310
diff --git a/keystoneauth1/exceptions/discovery.py b/keystoneauth1/exceptions/discovery.py
index 209009b..5bc6b06 100644
--- a/keystoneauth1/exceptions/discovery.py
+++ b/keystoneauth1/exceptions/discovery.py
@@ -10,10 +10,17 @@
10# License for the specific language governing permissions and limitations 10# License for the specific language governing permissions and limitations
11# under the License. 11# under the License.
12 12
13import os_service_types
14
13from keystoneauth1.exceptions import base 15from keystoneauth1.exceptions import base
14 16
17_SERVICE_TYPES = os_service_types.ServiceTypes()
18
15 19
16__all__ = ('DiscoveryFailure', 20__all__ = ('DiscoveryFailure',
21 'ImpliedVersionMismatch',
22 'ImpliedMinVersionMismatch',
23 'ImpliedMaxVersionMismatch',
17 'VersionNotAvailable') 24 'VersionNotAvailable')
18 25
19 26
@@ -23,3 +30,28 @@ class DiscoveryFailure(base.ClientException):
23 30
24class VersionNotAvailable(DiscoveryFailure): 31class VersionNotAvailable(DiscoveryFailure):
25 message = "Discovery failed. Requested version is not available." 32 message = "Discovery failed. Requested version is not available."
33
34
35class ImpliedVersionMismatch(ValueError):
36 label = 'version'
37
38 def __init__(self, service_type, implied, given):
39 super(ImpliedVersionMismatch, self).__init__(
40 "service_type {service_type} was given which implies"
41 " major API version {implied} but {label} of"
42 " {given} was also given. Please update your code"
43 " to use the official service_type {official_type}.".format(
44 service_type=service_type,
45 implied=str(implied[0]),
46 given=given,
47 label=self.label,
48 official_type=_SERVICE_TYPES.get_service_type(service_type),
49 ))
50
51
52class ImpliedMinVersionMismatch(ImpliedVersionMismatch):
53 label = 'min_version'
54
55
56class ImpliedMaxVersionMismatch(ImpliedVersionMismatch):
57 label = 'max_version'
diff --git a/keystoneauth1/identity/base.py b/keystoneauth1/identity/base.py
index 2912450..d73ef52 100644
--- a/keystoneauth1/identity/base.py
+++ b/keystoneauth1/identity/base.py
@@ -226,7 +226,7 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin):
226 allow = allow or {} 226 allow = allow or {}
227 227
228 min_version, max_version = discover._normalize_version_args( 228 min_version, max_version = discover._normalize_version_args(
229 None, min_version, max_version) 229 None, min_version, max_version, service_type=service_type)
230 230
231 # NOTE(jamielennox): if you specifically ask for requests to be sent to 231 # NOTE(jamielennox): if you specifically ask for requests to be sent to
232 # the auth url then we can ignore many of the checks. Typically if you 232 # the auth url then we can ignore many of the checks. Typically if you
@@ -365,7 +365,7 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin):
365 # Explode `version` into min_version and max_version - everything below 365 # Explode `version` into min_version and max_version - everything below
366 # here uses the latter rather than the former. 366 # here uses the latter rather than the former.
367 min_version, max_version = discover._normalize_version_args( 367 min_version, max_version = discover._normalize_version_args(
368 version, min_version, max_version) 368 version, min_version, max_version, service_type=service_type)
369 # Set discover_versions to False since we're only going to return 369 # Set discover_versions to False since we're only going to return
370 # a URL. Fetching the microversion data would be needlessly 370 # a URL. Fetching the microversion data would be needlessly
371 # expensive in the common case. However, discover_versions=False 371 # expensive in the common case. However, discover_versions=False
@@ -487,7 +487,7 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin):
487 # Explode `version` into min_version and max_version - everything below 487 # Explode `version` into min_version and max_version - everything below
488 # here uses the latter rather than the former. 488 # here uses the latter rather than the former.
489 min_version, max_version = discover._normalize_version_args( 489 min_version, max_version = discover._normalize_version_args(
490 version, min_version, max_version) 490 version, min_version, max_version, service_type=service_type)
491 # Using functools.partial here just to reduce copy-pasta of params 491 # Using functools.partial here just to reduce copy-pasta of params
492 get_endpoint_data = functools.partial( 492 get_endpoint_data = functools.partial(
493 self.get_endpoint_data, 493 self.get_endpoint_data,
diff --git a/keystoneauth1/tests/unit/identity/test_identity_common.py b/keystoneauth1/tests/unit/identity/test_identity_common.py
index 0e4ba6a..b208e54 100644
--- a/keystoneauth1/tests/unit/identity/test_identity_common.py
+++ b/keystoneauth1/tests/unit/identity/test_identity_common.py
@@ -1053,7 +1053,7 @@ class CommonIdentityTests(object):
1053 1053
1054 # We should only try to fetch the versioned discovery url once 1054 # We should only try to fetch the versioned discovery url once
1055 self.requests_mock.get( 1055 self.requests_mock.get(
1056 self.TEST_VOLUME.versions['v3'].discovery.public, resps) 1056 self.TEST_VOLUME.versions['v3'].discovery.public + '/', resps)
1057 1057
1058 data = a.get_endpoint_data(session=s, 1058 data = a.get_endpoint_data(session=s,
1059 service_type='volumev3', 1059 service_type='volumev3',
@@ -1128,7 +1128,7 @@ class CommonIdentityTests(object):
1128 1128
1129 # Fetch v2.0 first - since that doesn't match endpoint optimization, 1129 # Fetch v2.0 first - since that doesn't match endpoint optimization,
1130 # it should fetch the unversioned endpoint 1130 # it should fetch the unversioned endpoint
1131 v2_data = s.get_endpoint_data(service_type='volumev3', 1131 v2_data = s.get_endpoint_data(service_type='block-storage',
1132 interface='public', 1132 interface='public',
1133 min_version='2.0', 1133 min_version='2.0',
1134 max_version='2.latest', 1134 max_version='2.latest',
@@ -1179,10 +1179,10 @@ class CommonIdentityTests(object):
1179 self.requests_mock.get( 1179 self.requests_mock.get(
1180 self.TEST_VOLUME.unversioned.public + '/', json=disc) 1180 self.TEST_VOLUME.unversioned.public + '/', json=disc)
1181 1181
1182 # We're requesting version 2 of volumev3 to make sure we 1182 # We're requesting version 2 of block-storage to make sure we
1183 # trigger the logic constructing the unversioned endpoint from the 1183 # trigger the logic constructing the unversioned endpoint from the
1184 # versioned endpoint in the catalog 1184 # versioned endpoint in the catalog
1185 s.get_endpoint_data(service_type='volumev3', 1185 s.get_endpoint_data(service_type='block-storage',
1186 interface='public', 1186 interface='public',
1187 min_version='2.0', 1187 min_version='2.0',
1188 max_version='2.latest', 1188 max_version='2.latest',
@@ -1413,6 +1413,15 @@ class V3(CommonIdentityTests, utils.TestCase):
1413 internal=self.TEST_VOLUME.versions['v3'].service.internal, 1413 internal=self.TEST_VOLUME.versions['v3'].service.internal,
1414 region=region) 1414 region=region)
1415 1415
1416 # Add block-storage as a versioned endpoint so that we can test
1417 # versioned to unversioned inference.
1418 svc = token.add_service('block-storage')
1419 svc.add_standard_endpoints(
1420 admin=self.TEST_VOLUME.versions['v3'].service.admin,
1421 public=self.TEST_VOLUME.versions['v3'].service.public,
1422 internal=self.TEST_VOLUME.versions['v3'].service.internal,
1423 region=region)
1424
1416 svc = token.add_service('baremetal') 1425 svc = token.add_service('baremetal')
1417 svc.add_standard_endpoints( 1426 svc.add_standard_endpoints(
1418 internal=self.TEST_BAREMETAL_INTERNAL, 1427 internal=self.TEST_BAREMETAL_INTERNAL,
@@ -1490,6 +1499,15 @@ class V2(CommonIdentityTests, utils.TestCase):
1490 internal=self.TEST_VOLUME.versions['v3'].service.internal, 1499 internal=self.TEST_VOLUME.versions['v3'].service.internal,
1491 region=region) 1500 region=region)
1492 1501
1502 # Add block-storage as a versioned endpoint so that we can test
1503 # versioned to unversioned inferance.
1504 svc = token.add_service('block-storage')
1505 svc.add_endpoint(
1506 admin=self.TEST_VOLUME.versions['v3'].service.admin,
1507 public=self.TEST_VOLUME.versions['v3'].service.public,
1508 internal=self.TEST_VOLUME.versions['v3'].service.internal,
1509 region=region)
1510
1493 svc = token.add_service('baremetal') 1511 svc = token.add_service('baremetal')
1494 svc.add_endpoint( 1512 svc.add_endpoint(
1495 public=None, admin=None, 1513 public=None, admin=None,
diff --git a/keystoneauth1/tests/unit/test_discovery.py b/keystoneauth1/tests/unit/test_discovery.py
index e9d2701..53e6394 100644
--- a/keystoneauth1/tests/unit/test_discovery.py
+++ b/keystoneauth1/tests/unit/test_discovery.py
@@ -323,52 +323,63 @@ class DiscoverUtils(utils.TestCase):
323 323
324 def test_version_args(self): 324 def test_version_args(self):
325 """Validate _normalize_version_args.""" 325 """Validate _normalize_version_args."""
326 def assert_min_max(in_ver, in_min, in_max, out_min, out_max): 326 def assert_min_max(in_ver, in_min, in_max, in_type, out_min, out_max):
327 self.assertEqual( 327 self.assertEqual(
328 (out_min, out_max), 328 (out_min, out_max),
329 discover._normalize_version_args(in_ver, in_min, in_max)) 329 discover._normalize_version_args(
330 in_ver, in_min, in_max, service_type=in_type))
330 331
331 def normalize_raises(ver, min, max): 332 def normalize_raises(ver, min, max, in_type):
332 self.assertRaises(ValueError, 333 self.assertRaises(
333 discover._normalize_version_args, ver, min, max) 334 ValueError,
335 discover._normalize_version_args,
336 ver, min, max, service_type=in_type)
334 337
335 assert_min_max(None, None, None, 338 assert_min_max(None, None, None, None,
336 None, None) 339 None, None)
337 assert_min_max(None, None, 'v1.2', 340 assert_min_max(None, None, 'v1.2', None,
338 None, (1, 2)) 341 None, (1, 2))
339 assert_min_max(None, 'v1.2', 'latest', 342 assert_min_max(None, 'v1.2', 'latest', None,
340 (1, 2), (discover.LATEST, discover.LATEST)) 343 (1, 2), (discover.LATEST, discover.LATEST))
341 assert_min_max(None, 'v1.2', '1.6', 344 assert_min_max(None, 'v1.2', '1.6', None,
342 (1, 2), (1, 6)) 345 (1, 2), (1, 6))
343 assert_min_max(None, 'v1.2', '1.latest', 346 assert_min_max(None, 'v1.2', '1.latest', None,
344 (1, 2), (1, discover.LATEST)) 347 (1, 2), (1, discover.LATEST))
345 assert_min_max(None, 'latest', 'latest', 348 assert_min_max(None, 'latest', 'latest', None,
346 (discover.LATEST, discover.LATEST), 349 (discover.LATEST, discover.LATEST),
347 (discover.LATEST, discover.LATEST)) 350 (discover.LATEST, discover.LATEST))
348 assert_min_max(None, 'latest', None, 351 assert_min_max(None, 'latest', None, None,
349 (discover.LATEST, discover.LATEST), 352 (discover.LATEST, discover.LATEST),
350 (discover.LATEST, discover.LATEST)) 353 (discover.LATEST, discover.LATEST))
351 assert_min_max(None, (1, 2), None, 354 assert_min_max(None, (1, 2), None, None,
352 (1, 2), (discover.LATEST, discover.LATEST)) 355 (1, 2), (discover.LATEST, discover.LATEST))
353 assert_min_max('', ('1', '2'), (1, 6), 356 assert_min_max('', ('1', '2'), (1, 6), None,
354 (1, 2), (1, 6)) 357 (1, 2), (1, 6))
355 assert_min_max(None, ('1', '2'), (1, discover.LATEST), 358 assert_min_max(None, ('1', '2'), (1, discover.LATEST), None,
356 (1, 2), (1, discover.LATEST)) 359 (1, 2), (1, discover.LATEST))
357 assert_min_max('v1.2', '', None, 360 assert_min_max('v1.2', '', None, None,
358 (1, 2), (1, discover.LATEST)) 361 (1, 2), (1, discover.LATEST))
359 assert_min_max('1.latest', None, '', 362 assert_min_max('1.latest', None, '', None,
360 (1, discover.LATEST), (1, discover.LATEST)) 363 (1, discover.LATEST), (1, discover.LATEST))
361 assert_min_max('v1', None, None, 364 assert_min_max('v1', None, None, None,
362 (1, 0), (1, discover.LATEST)) 365 (1, 0), (1, discover.LATEST))
363 assert_min_max('latest', None, None, 366 assert_min_max('latest', None, None, None,
364 (discover.LATEST, discover.LATEST), 367 (discover.LATEST, discover.LATEST),
365 (discover.LATEST, discover.LATEST)) 368 (discover.LATEST, discover.LATEST))
366 369 assert_min_max(None, None, 'latest', 'volumev2',
367 normalize_raises('v1', 'v2', None) 370 (2, 0), (2, discover.LATEST))
368 normalize_raises('v1', None, 'v2') 371 assert_min_max(None, None, None, 'volumev2',
369 normalize_raises(None, 'latest', 'v1') 372 (2, 0), (2, discover.LATEST))
370 normalize_raises(None, 'v1.2', 'v1.1') 373
371 normalize_raises(None, 'v1.2', 1) 374 normalize_raises('v1', 'v2', None, None)
375 normalize_raises('v1', None, 'v2', None)
376 normalize_raises(None, 'latest', 'v1', None)
377 normalize_raises(None, 'v1.2', 'v1.1', None)
378 normalize_raises(None, 'v1.2', 1, None)
379 normalize_raises('v2', None, None, 'volumev3')
380 normalize_raises('v3', None, None, 'volumev2')
381 normalize_raises(None, 'v2', None, 'volumev3')
382 normalize_raises(None, None, 'v3', 'volumev2')
372 383
373 def test_version_to_string(self): 384 def test_version_to_string(self):
374 def assert_string(out, inp): 385 def assert_string(out, inp):