OpenStack Identity Authentication Library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_discovery.py 41KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. import json
  13. import re
  14. import mock
  15. from testtools import matchers
  16. from keystoneauth1 import adapter
  17. from keystoneauth1 import discover
  18. from keystoneauth1 import exceptions
  19. from keystoneauth1 import fixture
  20. from keystoneauth1 import noauth
  21. from keystoneauth1 import session
  22. from keystoneauth1.tests.unit import utils
  23. from keystoneauth1 import token_endpoint
  24. BASE_HOST = 'http://keystone.example.com'
  25. BASE_URL = "%s:5000/" % BASE_HOST
  26. UPDATED = '2013-03-06T00:00:00Z'
  27. TEST_SERVICE_CATALOG = [{
  28. "endpoints": [{
  29. "adminURL": "%s:8774/v1.0" % BASE_HOST,
  30. "region": "RegionOne",
  31. "internalURL": "%s://127.0.0.1:8774/v1.0" % BASE_HOST,
  32. "publicURL": "%s:8774/v1.0/" % BASE_HOST
  33. }],
  34. "type": "nova_compat",
  35. "name": "nova_compat"
  36. }, {
  37. "endpoints": [{
  38. "adminURL": "http://nova/novapi/admin",
  39. "region": "RegionOne",
  40. "internalURL": "http://nova/novapi/internal",
  41. "publicURL": "http://nova/novapi/public"
  42. }],
  43. "type": "compute",
  44. "name": "nova"
  45. }, {
  46. "endpoints": [{
  47. "adminURL": "http://glance/glanceapi/admin",
  48. "region": "RegionOne",
  49. "internalURL": "http://glance/glanceapi/internal",
  50. "publicURL": "http://glance/glanceapi/public"
  51. }],
  52. "type": "image",
  53. "name": "glance"
  54. }, {
  55. "endpoints": [{
  56. "adminURL": "%s:35357/v2.0" % BASE_HOST,
  57. "region": "RegionOne",
  58. "internalURL": "%s:5000/v2.0" % BASE_HOST,
  59. "publicURL": "%s:5000/v2.0" % BASE_HOST
  60. }],
  61. "type": "identity",
  62. "name": "keystone"
  63. }, {
  64. "endpoints": [{
  65. "adminURL": "http://swift/swiftapi/admin",
  66. "region": "RegionOne",
  67. "internalURL": "http://swift/swiftapi/internal",
  68. "publicURL": "http://swift/swiftapi/public"
  69. }],
  70. "type": "object-store",
  71. "name": "swift"
  72. }]
  73. V2_URL = "%sv2.0" % BASE_URL
  74. V2_VERSION = fixture.V2Discovery(V2_URL)
  75. V2_VERSION.updated_str = UPDATED
  76. V2_AUTH_RESPONSE = json.dumps({
  77. "access": {
  78. "token": {
  79. "expires": "2020-01-01T00:00:10.000123Z",
  80. "id": 'fakeToken',
  81. "tenant": {
  82. "id": '1'
  83. },
  84. },
  85. "user": {
  86. "id": 'test'
  87. },
  88. "serviceCatalog": TEST_SERVICE_CATALOG,
  89. },
  90. })
  91. V3_URL = "%sv3" % BASE_URL
  92. V3_VERSION = fixture.V3Discovery(V3_URL)
  93. V3_MEDIA_TYPES = V3_VERSION.media_types
  94. V3_VERSION.updated_str = UPDATED
  95. V3_AUTH_RESPONSE = json.dumps({
  96. "token": {
  97. "methods": [
  98. "token",
  99. "password"
  100. ],
  101. "expires_at": "2020-01-01T00:00:10.000123Z",
  102. "project": {
  103. "domain": {
  104. "id": '1',
  105. "name": 'test-domain'
  106. },
  107. "id": '1',
  108. "name": 'test-project'
  109. },
  110. "user": {
  111. "domain": {
  112. "id": '1',
  113. "name": 'test-domain'
  114. },
  115. "id": '1',
  116. "name": 'test-user'
  117. },
  118. "issued_at": "2013-05-29T16:55:21.468960Z",
  119. },
  120. })
  121. CINDER_EXAMPLES = {
  122. "versions": [
  123. {
  124. "status": discover.Status.CURRENT,
  125. "updated": "2012-01-04T11:33:21Z",
  126. "id": "v1.0",
  127. "links": [
  128. {
  129. "href": "%sv1/" % BASE_URL,
  130. "rel": "self"
  131. }
  132. ]
  133. },
  134. {
  135. "status": discover.Status.CURRENT,
  136. "updated": "2012-11-21T11:33:21Z",
  137. "id": "v2.0",
  138. "links": [
  139. {
  140. "href": "%sv2/" % BASE_URL,
  141. "rel": "self"
  142. }
  143. ]
  144. },
  145. {
  146. "status": discover.Status.CURRENT,
  147. "updated": "2012-11-21T11:33:21Z",
  148. "id": "v3.0",
  149. "version": "3.27",
  150. "min_version": "3.0",
  151. "next_min_version": "3.4",
  152. "not_before": "2019-12-31",
  153. "links": [
  154. {
  155. "href": BASE_URL,
  156. "rel": "collection"
  157. },
  158. {
  159. "href": "%sv3/" % BASE_URL,
  160. "rel": "self"
  161. }
  162. ]
  163. }
  164. ]
  165. }
  166. GLANCE_EXAMPLES = {
  167. "versions": [
  168. {
  169. "status": discover.Status.CURRENT,
  170. "id": "v2.2",
  171. "links": [
  172. {
  173. "href": "%sv2/" % BASE_URL,
  174. "rel": "self"
  175. }
  176. ]
  177. },
  178. {
  179. "status": discover.Status.SUPPORTED,
  180. "id": "v2.1",
  181. "links": [
  182. {
  183. "href": "%sv2/" % BASE_URL,
  184. "rel": "self"
  185. }
  186. ]
  187. },
  188. {
  189. "status": discover.Status.SUPPORTED,
  190. "id": "v2.0",
  191. "links": [
  192. {
  193. "href": "%sv2/" % BASE_URL,
  194. "rel": "self"
  195. }
  196. ]
  197. },
  198. {
  199. "status": discover.Status.CURRENT,
  200. "id": "v1.1",
  201. "links": [
  202. {
  203. "href": "%sv1/" % BASE_URL,
  204. "rel": "self"
  205. }
  206. ]
  207. },
  208. {
  209. "status": discover.Status.SUPPORTED,
  210. "id": "v1.0",
  211. "links": [
  212. {
  213. "href": "%sv1/" % BASE_URL,
  214. "rel": "self"
  215. }
  216. ]
  217. }
  218. ]
  219. }
  220. def _create_version_list(versions):
  221. return {'versions': {'values': versions}}
  222. def _create_single_version(version):
  223. return {'version': version}
  224. V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION])
  225. V2_VERSION_LIST = _create_version_list([V2_VERSION])
  226. V3_VERSION_ENTRY = _create_single_version(V3_VERSION)
  227. V2_VERSION_ENTRY = _create_single_version(V2_VERSION)
  228. class CatalogHackTests(utils.TestCase):
  229. TEST_URL = 'http://keystone.server:5000/v2.0'
  230. OTHER_URL = 'http://other.server:5000/path'
  231. IDENTITY = 'identity'
  232. BASE_URL = 'http://keystone.server:5000/'
  233. V2_URL = BASE_URL + 'v2.0'
  234. V3_URL = BASE_URL + 'v3'
  235. def setUp(self):
  236. super(CatalogHackTests, self).setUp()
  237. self.hacks = discover._VersionHacks()
  238. self.hacks.add_discover_hack(self.IDENTITY,
  239. re.compile('/v2.0/?$'),
  240. '/')
  241. def test_version_hacks(self):
  242. self.assertEqual(self.BASE_URL,
  243. self.hacks.get_discover_hack(self.IDENTITY,
  244. self.V2_URL))
  245. self.assertEqual(self.BASE_URL,
  246. self.hacks.get_discover_hack(self.IDENTITY,
  247. self.V2_URL + '/'))
  248. self.assertEqual(self.OTHER_URL,
  249. self.hacks.get_discover_hack(self.IDENTITY,
  250. self.OTHER_URL))
  251. def test_ignored_non_service_type(self):
  252. self.assertEqual(self.V2_URL,
  253. self.hacks.get_discover_hack('other', self.V2_URL))
  254. class DiscoverUtils(utils.TestCase):
  255. def test_version_number(self):
  256. def assertVersion(out, inp):
  257. self.assertEqual(out, discover.normalize_version_number(inp))
  258. def versionRaises(inp):
  259. self.assertRaises(TypeError,
  260. discover.normalize_version_number,
  261. inp)
  262. assertVersion((1, 2), 'v1.2')
  263. assertVersion((11, 0), 'v11')
  264. assertVersion((1, 2), '1.2')
  265. assertVersion((1, 5, 1), '1.5.1')
  266. assertVersion((1, 0), '1')
  267. assertVersion((1, 0), 1)
  268. assertVersion((5, 2), 5.2)
  269. assertVersion((3, 20), '3.20')
  270. assertVersion((6, 1), (6, 1))
  271. assertVersion((1, 40), [1, 40])
  272. assertVersion((1, 0), (1,))
  273. assertVersion((1, 0), ['1'])
  274. assertVersion((discover.LATEST, discover.LATEST), 'latest')
  275. assertVersion((discover.LATEST, discover.LATEST), ['latest'])
  276. assertVersion((discover.LATEST, discover.LATEST), discover.LATEST)
  277. assertVersion((discover.LATEST, discover.LATEST), (discover.LATEST,))
  278. assertVersion((10, discover.LATEST), '10.latest')
  279. assertVersion((10, discover.LATEST), (10, 'latest'))
  280. assertVersion((10, discover.LATEST), (10, discover.LATEST))
  281. versionRaises(None)
  282. versionRaises('hello')
  283. versionRaises('1.a')
  284. versionRaises('vacuum')
  285. versionRaises('')
  286. versionRaises(('1', 'a'))
  287. def test_version_args(self):
  288. """Validate _normalize_version_args."""
  289. def assert_min_max(in_ver, in_min, in_max, in_type, out_min, out_max):
  290. self.assertEqual(
  291. (out_min, out_max),
  292. discover._normalize_version_args(
  293. in_ver, in_min, in_max, service_type=in_type))
  294. def normalize_raises(ver, min, max, in_type):
  295. self.assertRaises(
  296. ValueError,
  297. discover._normalize_version_args,
  298. ver, min, max, service_type=in_type)
  299. assert_min_max(None, None, None, None,
  300. None, None)
  301. assert_min_max(None, None, 'v1.2', None,
  302. None, (1, 2))
  303. assert_min_max(None, 'v1.2', 'latest', None,
  304. (1, 2), (discover.LATEST, discover.LATEST))
  305. assert_min_max(None, 'v1.2', '1.6', None,
  306. (1, 2), (1, 6))
  307. assert_min_max(None, 'v1.2', '1.latest', None,
  308. (1, 2), (1, discover.LATEST))
  309. assert_min_max(None, 'latest', 'latest', None,
  310. (discover.LATEST, discover.LATEST),
  311. (discover.LATEST, discover.LATEST))
  312. assert_min_max(None, 'latest', None, None,
  313. (discover.LATEST, discover.LATEST),
  314. (discover.LATEST, discover.LATEST))
  315. assert_min_max(None, (1, 2), None, None,
  316. (1, 2), (discover.LATEST, discover.LATEST))
  317. assert_min_max('', ('1', '2'), (1, 6), None,
  318. (1, 2), (1, 6))
  319. assert_min_max(None, ('1', '2'), (1, discover.LATEST), None,
  320. (1, 2), (1, discover.LATEST))
  321. assert_min_max('v1.2', '', None, None,
  322. (1, 2), (1, discover.LATEST))
  323. assert_min_max('1.latest', None, '', None,
  324. (1, discover.LATEST), (1, discover.LATEST))
  325. assert_min_max('v1', None, None, None,
  326. (1, 0), (1, discover.LATEST))
  327. assert_min_max('latest', None, None, None,
  328. (discover.LATEST, discover.LATEST),
  329. (discover.LATEST, discover.LATEST))
  330. assert_min_max(None, None, 'latest', 'volumev2',
  331. (2, 0), (2, discover.LATEST))
  332. assert_min_max(None, None, None, 'volumev2',
  333. (2, 0), (2, discover.LATEST))
  334. normalize_raises('v1', 'v2', None, None)
  335. normalize_raises('v1', None, 'v2', None)
  336. normalize_raises(None, 'latest', 'v1', None)
  337. normalize_raises(None, 'v1.2', 'v1.1', None)
  338. normalize_raises(None, 'v1.2', 1, None)
  339. normalize_raises('v2', None, None, 'volumev3')
  340. normalize_raises('v3', None, None, 'volumev2')
  341. normalize_raises(None, 'v2', None, 'volumev3')
  342. normalize_raises(None, None, 'v3', 'volumev2')
  343. def test_version_to_string(self):
  344. def assert_string(out, inp):
  345. self.assertEqual(out, discover.version_to_string(inp))
  346. assert_string('latest', (discover.LATEST,))
  347. assert_string('latest', (discover.LATEST, discover.LATEST))
  348. assert_string('latest',
  349. (discover.LATEST, discover.LATEST, discover.LATEST))
  350. assert_string('1', (1,))
  351. assert_string('1.2', (1, 2))
  352. assert_string('1.latest', (1, discover.LATEST))
  353. def test_version_between(self):
  354. def good(minver, maxver, cand):
  355. self.assertTrue(discover._version_between(minver, maxver, cand))
  356. def bad(minver, maxver, cand):
  357. self.assertFalse(discover._version_between(minver, maxver, cand))
  358. def exc(excls, minver, maxver, cand):
  359. self.assertRaises(excls,
  360. discover._version_between, minver, maxver, cand)
  361. # candidate required
  362. exc(ValueError, (1, 0), (1, 0), None)
  363. exc(ValueError, 'v1.0', '1.0', '')
  364. # malformed candidate
  365. exc(TypeError, None, None, 'bogus')
  366. exc(TypeError, None, None, (1, 'two'))
  367. # malformed min_version
  368. exc(TypeError, 'bogus', None, (1, 0))
  369. exc(TypeError, (1, 'two'), None, (1, 0))
  370. # malformed max_version
  371. exc(TypeError, None, 'bogus', (1, 0))
  372. exc(TypeError, None, (1, 'two'), (1, 0))
  373. # fail on minimum
  374. bad((2, 4), None, (1, 55))
  375. bad('v2.4', '', '2.3')
  376. bad('latest', None, (2, 3000))
  377. bad((2, discover.LATEST), '', 'v2.3000')
  378. bad((2, 3000), '', (1, discover.LATEST))
  379. bad('latest', None, 'v1000.latest')
  380. # fail on maximum
  381. bad(None, (2, 4), (2, 5))
  382. bad('', 'v2.4', '2.5')
  383. bad(None, (2, discover.LATEST), (3, 0))
  384. bad('', '2000.latest', 'latest')
  385. # candidate matches a bound
  386. good((1, 0), (1, 0), (1, 0))
  387. good('1.0', '2.9', '1.0')
  388. good('v1.0', 'v2.9', 'v2.9')
  389. # properly in between
  390. good((1, 0), (1, 10), (1, 2))
  391. good('1', '2', '1.2')
  392. # no lower bound
  393. good(None, (2, 5), (2, 3))
  394. # no upper bound
  395. good('2.5', '', '2.6')
  396. # no bounds at all
  397. good('', '', 'v1')
  398. good(None, None, (999, 999))
  399. good(None, None, 'latest')
  400. # Various good 'latest' scenarios
  401. good((discover.LATEST, discover.LATEST),
  402. (discover.LATEST, discover.LATEST),
  403. (discover.LATEST, discover.LATEST))
  404. good((discover.LATEST, discover.LATEST), None,
  405. (discover.LATEST, discover.LATEST))
  406. good('', 'latest', 'latest')
  407. good('2.latest', '3.latest', '3.0')
  408. good('2.latest', None, (55, 66))
  409. good(None, '3.latest', '3.9999')
  410. class VersionDataTests(utils.TestCase):
  411. def setUp(self):
  412. super(VersionDataTests, self).setUp()
  413. self.session = session.Session()
  414. def test_version_data_basics(self):
  415. examples = {'keystone': V3_VERSION_LIST,
  416. 'cinder': CINDER_EXAMPLES,
  417. 'glance': GLANCE_EXAMPLES}
  418. for path, data in examples.items():
  419. url = "%s%s" % (BASE_URL, path)
  420. mock = self.requests_mock.get(url, status_code=300, json=data)
  421. disc = discover.Discover(self.session, url)
  422. raw_data = disc.raw_version_data()
  423. clean_data = disc.version_data()
  424. for v in raw_data:
  425. for n in ('id', 'status', 'links'):
  426. msg = '%s missing from %s version data' % (n, path)
  427. self.assertThat(v, matchers.Annotate(msg,
  428. matchers.Contains(n)))
  429. for v in clean_data:
  430. for n in ('version', 'url', 'raw_status'):
  431. msg = '%s missing from %s version data' % (n, path)
  432. self.assertThat(v, matchers.Annotate(msg,
  433. matchers.Contains(n)))
  434. self.assertTrue(mock.called_once)
  435. def test_version_data_unknown(self):
  436. discovery_fixture = fixture.V3Discovery(V3_URL)
  437. discovery_fixture.status = 'hungry'
  438. discovery_doc = _create_single_version(discovery_fixture)
  439. self.requests_mock.get(V3_URL, status_code=200, json=discovery_doc)
  440. disc = discover.Discover(self.session, V3_URL)
  441. clean_data = disc.version_data(allow_unknown=True)
  442. self.assertEqual(discover.Status.UNKNOWN, clean_data[0]['status'])
  443. def test_version_data_individual(self):
  444. mock = self.requests_mock.get(V3_URL,
  445. status_code=200,
  446. json=V3_VERSION_ENTRY)
  447. disc = discover.Discover(self.session, V3_URL)
  448. raw_data = disc.raw_version_data()
  449. clean_data = disc.version_data()
  450. for v in raw_data:
  451. self.assertEqual(v['id'], 'v3.0')
  452. self.assertEqual(v['status'], 'stable')
  453. self.assertIn('media-types', v)
  454. self.assertIn('links', v)
  455. for v in clean_data:
  456. self.assertEqual(v['version'], (3, 0))
  457. self.assertEqual(v['status'], discover.Status.CURRENT)
  458. self.assertEqual(v['raw_status'], 'stable')
  459. self.assertEqual(v['url'], V3_URL)
  460. self.assertTrue(mock.called_once)
  461. def test_version_data_microversions(self):
  462. """Validate [min_|max_]version conversion to {min|max}_microversion."""
  463. def setup_mock(versions_in):
  464. # Set up the test data with the input version data
  465. jsondata = {
  466. "versions": [
  467. dict(
  468. {
  469. "status": discover.Status.CURRENT,
  470. "id": "v2.2",
  471. "links": [
  472. {
  473. "href": V3_URL,
  474. "rel": "self"
  475. }
  476. ]
  477. },
  478. **versions_in
  479. )
  480. ]
  481. }
  482. self.requests_mock.get(
  483. V3_URL, status_code=200, json=jsondata)
  484. def test_ok(versions_in, versions_out):
  485. setup_mock(versions_in)
  486. # Ensure the output contains the expected microversions
  487. self.assertEqual(
  488. [
  489. dict(
  490. {
  491. 'collection': None,
  492. 'version': (2, 2),
  493. 'url': V3_URL,
  494. 'status': discover.Status.CURRENT,
  495. 'raw_status': discover.Status.CURRENT,
  496. },
  497. **versions_out
  498. )
  499. ],
  500. discover.Discover(self.session, V3_URL).version_data())
  501. def test_exc(versions_in):
  502. setup_mock(versions_in)
  503. # Ensure TypeError is raised
  504. self.assertRaises(
  505. TypeError,
  506. discover.Discover(self.session, V3_URL).version_data)
  507. # no version info in input
  508. test_ok({},
  509. {'min_microversion': None, 'max_microversion': None,
  510. 'next_min_version': None, 'not_before': None})
  511. # version => max_microversion
  512. test_ok({'version': '2.2'},
  513. {'min_microversion': None, 'max_microversion': (2, 2),
  514. 'next_min_version': None, 'not_before': None})
  515. # max_version supersedes version (even if malformed). min_version &
  516. # normalization.
  517. test_ok({'min_version': '2', 'version': 'foo', 'max_version': '2.2'},
  518. {'min_microversion': (2, 0), 'max_microversion': (2, 2),
  519. 'next_min_version': None, 'not_before': None})
  520. # Edge case: min/max_version ignored if present but "empty"; version
  521. # used for max_microversion.
  522. test_ok({'min_version': '', 'version': '2.1', 'max_version': ''},
  523. {'min_microversion': None, 'max_microversion': (2, 1),
  524. 'next_min_version': None, 'not_before': None})
  525. # next_min_version set
  526. test_ok({'min_version': '2', 'max_version': '2.2',
  527. 'next_min_version': '2.1', 'not_before': '2019-07-01'},
  528. {'min_microversion': (2, 0), 'max_microversion': (2, 2),
  529. 'next_min_version': (2, 1), 'not_before': '2019-07-01'})
  530. # Badly-formatted min_version
  531. test_exc({'min_version': 'foo', 'max_version': '2.1'})
  532. # Badly-formatted max_version
  533. test_exc({'min_version': '2.1', 'max_version': 'foo'})
  534. # Badly-formatted version (when max_version omitted)
  535. test_exc({'min_version': '2.1', 'version': 'foo'})
  536. # Badly-formatted next_min_version
  537. test_exc({'next_min_version': 'bogus', 'not_before': '2019-07-01'})
  538. def test_endpoint_data_noauth_discover(self):
  539. mock = self.requests_mock.get(
  540. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  541. plugin = noauth.NoAuth()
  542. data = plugin.get_endpoint_data(self.session, endpoint_override=V3_URL)
  543. self.assertEqual(data.api_version, (3, 0))
  544. self.assertEqual(data.url, V3_URL)
  545. self.assertEqual(
  546. plugin.get_api_major_version(
  547. self.session, endpoint_override=V3_URL),
  548. (3, 0))
  549. self.assertEqual(
  550. plugin.get_endpoint(self.session, endpoint_override=V3_URL),
  551. V3_URL)
  552. self.assertTrue(mock.called_once)
  553. def test_endpoint_data_noauth_no_discover(self):
  554. plugin = noauth.NoAuth()
  555. data = plugin.get_endpoint_data(
  556. self.session, endpoint_override=V3_URL, discover_versions=False)
  557. self.assertEqual(data.api_version, (3, 0))
  558. self.assertEqual(data.url, V3_URL)
  559. self.assertEqual(
  560. plugin.get_api_major_version(
  561. self.session, endpoint_override=V3_URL),
  562. (3, 0))
  563. self.assertEqual(
  564. plugin.get_endpoint(self.session, endpoint_override=V3_URL),
  565. V3_URL)
  566. def test_endpoint_data_noauth_adapter(self):
  567. mock = self.requests_mock.get(
  568. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  569. client = adapter.Adapter(
  570. session.Session(noauth.NoAuth()),
  571. endpoint_override=V3_URL)
  572. data = client.get_endpoint_data()
  573. self.assertEqual(data.api_version, (3, 0))
  574. self.assertEqual(data.url, V3_URL)
  575. self.assertEqual(client.get_api_major_version(), (3, 0))
  576. self.assertEqual(client.get_endpoint(), V3_URL)
  577. self.assertTrue(mock.called_once)
  578. def test_endpoint_data_token_endpoint_discover(self):
  579. mock = self.requests_mock.get(
  580. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  581. plugin = token_endpoint.Token(endpoint=V3_URL, token='bogus')
  582. data = plugin.get_endpoint_data(self.session)
  583. self.assertEqual(data.api_version, (3, 0))
  584. self.assertEqual(data.url, V3_URL)
  585. self.assertEqual(plugin.get_api_major_version(self.session), (3, 0))
  586. self.assertEqual(plugin.get_endpoint(self.session), V3_URL)
  587. self.assertTrue(mock.called_once)
  588. def test_endpoint_data_token_endpoint_no_discover(self):
  589. plugin = token_endpoint.Token(endpoint=V3_URL, token='bogus')
  590. data = plugin.get_endpoint_data(self.session, discover_versions=False)
  591. self.assertEqual(data.api_version, (3, 0))
  592. self.assertEqual(data.url, V3_URL)
  593. self.assertEqual(plugin.get_api_major_version(self.session), (3, 0))
  594. self.assertEqual(plugin.get_endpoint(self.session), V3_URL)
  595. def test_endpoint_data_token_endpoint_adapter(self):
  596. mock = self.requests_mock.get(
  597. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  598. plugin = token_endpoint.Token(endpoint=V3_URL, token='bogus')
  599. client = adapter.Adapter(session.Session(plugin))
  600. data = client.get_endpoint_data()
  601. self.assertEqual(data.api_version, (3, 0))
  602. self.assertEqual(data.url, V3_URL)
  603. self.assertEqual(client.get_api_major_version(), (3, 0))
  604. self.assertEqual(client.get_endpoint(), V3_URL)
  605. self.assertTrue(mock.called_once)
  606. def test_data_for_url(self):
  607. mock = self.requests_mock.get(V3_URL,
  608. status_code=200,
  609. json=V3_VERSION_ENTRY)
  610. disc = discover.Discover(self.session, V3_URL)
  611. for url in (V3_URL, V3_URL + '/'):
  612. data = disc.versioned_data_for(url=url)
  613. self.assertEqual(data['version'], (3, 0))
  614. self.assertEqual(data['raw_status'], 'stable')
  615. self.assertEqual(data['url'], V3_URL)
  616. self.assertTrue(mock.called_once)
  617. def test_data_for_no_version(self):
  618. mock = self.requests_mock.get(V3_URL,
  619. status_code=200,
  620. json=V3_VERSION_ENTRY)
  621. disc = discover.Discover(self.session, V3_URL)
  622. data = disc.versioned_data_for()
  623. self.assertEqual(data['version'], (3, 0))
  624. self.assertEqual(data['raw_status'], 'stable')
  625. self.assertEqual(data['url'], V3_URL)
  626. self.assertRaises(TypeError, disc.data_for, version=None)
  627. self.assertTrue(mock.called_once)
  628. def test_keystone_version_data(self):
  629. mock = self.requests_mock.get(BASE_URL,
  630. status_code=300,
  631. json=V3_VERSION_LIST)
  632. disc = discover.Discover(self.session, BASE_URL)
  633. raw_data = disc.raw_version_data()
  634. clean_data = disc.version_data()
  635. self.assertEqual(2, len(raw_data))
  636. self.assertEqual(2, len(clean_data))
  637. for v in raw_data:
  638. self.assertIn(v['id'], ('v2.0', 'v3.0'))
  639. self.assertEqual(v['updated'], UPDATED)
  640. self.assertEqual(v['status'], 'stable')
  641. if v['id'] == 'v3.0':
  642. self.assertEqual(v['media-types'], V3_MEDIA_TYPES)
  643. for v in clean_data:
  644. self.assertIn(v['version'], ((2, 0), (3, 0)))
  645. self.assertEqual(v['raw_status'], 'stable')
  646. valid_v3_versions = (
  647. disc.data_for('v3.0'),
  648. disc.data_for('3.latest'),
  649. disc.data_for('latest'),
  650. disc.versioned_data_for(min_version='v3.0',
  651. max_version='v3.latest'),
  652. disc.versioned_data_for(min_version='3'),
  653. disc.versioned_data_for(min_version='3.latest'),
  654. disc.versioned_data_for(min_version='latest'),
  655. disc.versioned_data_for(min_version='3.latest',
  656. max_version='latest'),
  657. disc.versioned_data_for(min_version='latest',
  658. max_version='latest'),
  659. disc.versioned_data_for(min_version=2),
  660. disc.versioned_data_for(min_version='2.latest'))
  661. for version in valid_v3_versions:
  662. self.assertEqual((3, 0), version['version'])
  663. self.assertEqual('stable', version['raw_status'])
  664. self.assertEqual(V3_URL, version['url'])
  665. valid_v2_versions = (
  666. disc.data_for(2),
  667. disc.data_for('2.latest'),
  668. disc.versioned_data_for(min_version=2,
  669. max_version=(2, discover.LATEST)),
  670. disc.versioned_data_for(min_version='2.latest',
  671. max_version='2.latest'))
  672. for version in valid_v2_versions:
  673. self.assertEqual((2, 0), version['version'])
  674. self.assertEqual('stable', version['raw_status'])
  675. self.assertEqual(V2_URL, version['url'])
  676. self.assertIsNone(disc.url_for('v4'))
  677. self.assertIsNone(disc.versioned_url_for(
  678. min_version='v4', max_version='v4.latest'))
  679. self.assertEqual(V3_URL, disc.url_for('v3'))
  680. self.assertEqual(V3_URL, disc.versioned_url_for(
  681. min_version='v3', max_version='v3.latest'))
  682. self.assertEqual(V2_URL, disc.url_for('v2'))
  683. self.assertEqual(V2_URL, disc.versioned_url_for(
  684. min_version='v2', max_version='v2.latest'))
  685. self.assertTrue(mock.called_once)
  686. def test_cinder_version_data(self):
  687. mock = self.requests_mock.get(BASE_URL,
  688. status_code=300,
  689. json=CINDER_EXAMPLES)
  690. disc = discover.Discover(self.session, BASE_URL)
  691. raw_data = disc.raw_version_data()
  692. clean_data = disc.version_data()
  693. self.assertEqual(3, len(raw_data))
  694. for v in raw_data:
  695. self.assertEqual(v['status'], discover.Status.CURRENT)
  696. if v['id'] == 'v1.0':
  697. self.assertEqual(v['updated'], '2012-01-04T11:33:21Z')
  698. elif v['id'] == 'v2.0':
  699. self.assertEqual(v['updated'], '2012-11-21T11:33:21Z')
  700. elif v['id'] == 'v3.0':
  701. self.assertEqual(v['updated'], '2012-11-21T11:33:21Z')
  702. else:
  703. self.fail("Invalid version found")
  704. v1_url = "%sv1/" % BASE_URL
  705. v2_url = "%sv2/" % BASE_URL
  706. v3_url = "%sv3/" % BASE_URL
  707. self.assertEqual(clean_data, [
  708. {
  709. 'collection': None,
  710. 'max_microversion': None,
  711. 'min_microversion': None,
  712. 'next_min_version': None,
  713. 'not_before': None,
  714. 'version': (1, 0),
  715. 'url': v1_url,
  716. 'status': discover.Status.CURRENT,
  717. 'raw_status': discover.Status.CURRENT,
  718. },
  719. {
  720. 'collection': None,
  721. 'max_microversion': None,
  722. 'min_microversion': None,
  723. 'next_min_version': None,
  724. 'not_before': None,
  725. 'version': (2, 0),
  726. 'url': v2_url,
  727. 'status': discover.Status.CURRENT,
  728. 'raw_status': discover.Status.CURRENT,
  729. },
  730. {
  731. 'collection': BASE_URL,
  732. 'max_microversion': (3, 27),
  733. 'min_microversion': (3, 0),
  734. 'next_min_version': (3, 4),
  735. 'not_before': u'2019-12-31',
  736. 'version': (3, 0),
  737. 'url': v3_url,
  738. 'status': discover.Status.CURRENT,
  739. 'raw_status': discover.Status.CURRENT,
  740. },
  741. ])
  742. for version in (disc.data_for('v2.0'),
  743. disc.versioned_data_for(min_version='v2.0',
  744. max_version='v2.latest')):
  745. self.assertEqual((2, 0), version['version'])
  746. self.assertEqual(discover.Status.CURRENT, version['raw_status'])
  747. self.assertEqual(v2_url, version['url'])
  748. for version in (disc.data_for(1),
  749. disc.versioned_data_for(
  750. min_version=(1,),
  751. max_version=(1, discover.LATEST))):
  752. self.assertEqual((1, 0), version['version'])
  753. self.assertEqual(discover.Status.CURRENT, version['raw_status'])
  754. self.assertEqual(v1_url, version['url'])
  755. self.assertIsNone(disc.url_for('v4'))
  756. self.assertIsNone(disc.versioned_url_for(min_version='v4',
  757. max_version='v4.latest'))
  758. self.assertEqual(v3_url, disc.url_for('v3'))
  759. self.assertEqual(v3_url, disc.versioned_url_for(
  760. min_version='v3', max_version='v3.latest'))
  761. self.assertEqual(v2_url, disc.url_for('v2'))
  762. self.assertEqual(v2_url, disc.versioned_url_for(
  763. min_version='v2', max_version='v2.latest'))
  764. self.assertEqual(v1_url, disc.url_for('v1'))
  765. self.assertEqual(v1_url, disc.versioned_url_for(
  766. min_version='v1', max_version='v1.latest'))
  767. self.assertTrue(mock.called_once)
  768. def test_glance_version_data(self):
  769. mock = self.requests_mock.get(BASE_URL,
  770. status_code=200,
  771. json=GLANCE_EXAMPLES)
  772. disc = discover.Discover(self.session, BASE_URL)
  773. raw_data = disc.raw_version_data()
  774. clean_data = disc.version_data()
  775. self.assertEqual(5, len(raw_data))
  776. for v in raw_data:
  777. if v['id'] in ('v2.2', 'v1.1'):
  778. self.assertEqual(v['status'], discover.Status.CURRENT)
  779. elif v['id'] in ('v2.1', 'v2.0', 'v1.0'):
  780. self.assertEqual(v['status'], discover.Status.SUPPORTED)
  781. else:
  782. self.fail("Invalid version found")
  783. v1_url = '%sv1/' % BASE_URL
  784. v2_url = '%sv2/' % BASE_URL
  785. self.assertEqual(clean_data, [
  786. {
  787. 'collection': None,
  788. 'max_microversion': None,
  789. 'min_microversion': None,
  790. 'next_min_version': None,
  791. 'not_before': None,
  792. 'version': (1, 0),
  793. 'url': v1_url,
  794. 'status': discover.Status.SUPPORTED,
  795. 'raw_status': discover.Status.SUPPORTED,
  796. },
  797. {
  798. 'collection': None,
  799. 'max_microversion': None,
  800. 'min_microversion': None,
  801. 'next_min_version': None,
  802. 'not_before': None,
  803. 'version': (1, 1),
  804. 'url': v1_url,
  805. 'status': discover.Status.CURRENT,
  806. 'raw_status': discover.Status.CURRENT,
  807. },
  808. {
  809. 'collection': None,
  810. 'max_microversion': None,
  811. 'min_microversion': None,
  812. 'next_min_version': None,
  813. 'not_before': None,
  814. 'version': (2, 0),
  815. 'url': v2_url,
  816. 'status': discover.Status.SUPPORTED,
  817. 'raw_status': discover.Status.SUPPORTED,
  818. },
  819. {
  820. 'collection': None,
  821. 'max_microversion': None,
  822. 'min_microversion': None,
  823. 'next_min_version': None,
  824. 'not_before': None,
  825. 'version': (2, 1),
  826. 'url': v2_url,
  827. 'status': discover.Status.SUPPORTED,
  828. 'raw_status': discover.Status.SUPPORTED,
  829. },
  830. {
  831. 'collection': None,
  832. 'max_microversion': None,
  833. 'min_microversion': None,
  834. 'next_min_version': None,
  835. 'not_before': None,
  836. 'version': (2, 2),
  837. 'url': v2_url,
  838. 'status': discover.Status.CURRENT,
  839. 'raw_status': discover.Status.CURRENT,
  840. },
  841. ])
  842. for ver in (2, 2.1, 2.2):
  843. for version in (disc.data_for(ver),
  844. disc.versioned_data_for(
  845. min_version=ver,
  846. max_version=(2, discover.LATEST))):
  847. self.assertEqual((2, 2), version['version'])
  848. self.assertEqual(
  849. discover.Status.CURRENT, version['raw_status']
  850. )
  851. self.assertEqual(v2_url, version['url'])
  852. self.assertEqual(v2_url, disc.url_for(ver))
  853. for ver in (1, 1.1):
  854. for version in (disc.data_for(ver),
  855. disc.versioned_data_for(
  856. min_version=ver,
  857. max_version=(1, discover.LATEST))):
  858. self.assertEqual((1, 1), version['version'])
  859. self.assertEqual(
  860. discover.Status.CURRENT, version['raw_status']
  861. )
  862. self.assertEqual(v1_url, version['url'])
  863. self.assertEqual(v1_url, disc.url_for(ver))
  864. self.assertIsNone(disc.url_for('v3'))
  865. self.assertIsNone(disc.versioned_url_for(min_version='v3',
  866. max_version='v3.latest'))
  867. self.assertIsNone(disc.url_for('v2.3'))
  868. self.assertIsNone(disc.versioned_url_for(min_version='v2.3',
  869. max_version='v2.latest'))
  870. self.assertTrue(mock.called_once)
  871. def test_allow_deprecated(self):
  872. status = 'deprecated'
  873. version_list = [{'id': 'v3.0',
  874. 'links': [{'href': V3_URL, 'rel': 'self'}],
  875. 'media-types': V3_MEDIA_TYPES,
  876. 'status': status,
  877. 'updated': UPDATED}]
  878. self.requests_mock.get(BASE_URL, json={'versions': version_list})
  879. disc = discover.Discover(self.session, BASE_URL)
  880. # deprecated is allowed by default
  881. versions = disc.version_data(allow_deprecated=False)
  882. self.assertEqual(0, len(versions))
  883. versions = disc.version_data(allow_deprecated=True)
  884. self.assertEqual(1, len(versions))
  885. self.assertEqual(status, versions[0]['raw_status'])
  886. self.assertEqual(V3_URL, versions[0]['url'])
  887. self.assertEqual((3, 0), versions[0]['version'])
  888. def test_allow_experimental(self):
  889. status = 'experimental'
  890. version_list = [{'id': 'v3.0',
  891. 'links': [{'href': V3_URL, 'rel': 'self'}],
  892. 'media-types': V3_MEDIA_TYPES,
  893. 'status': status,
  894. 'updated': UPDATED}]
  895. self.requests_mock.get(BASE_URL, json={'versions': version_list})
  896. disc = discover.Discover(self.session, BASE_URL)
  897. versions = disc.version_data()
  898. self.assertEqual(0, len(versions))
  899. versions = disc.version_data(allow_experimental=True)
  900. self.assertEqual(1, len(versions))
  901. self.assertEqual(status, versions[0]['raw_status'])
  902. self.assertEqual(V3_URL, versions[0]['url'])
  903. self.assertEqual((3, 0), versions[0]['version'])
  904. def test_allow_unknown(self):
  905. status = 'abcdef'
  906. version_list = fixture.DiscoveryList(BASE_URL,
  907. v2=False,
  908. v3_status=status)
  909. self.requests_mock.get(BASE_URL, json=version_list)
  910. disc = discover.Discover(self.session, BASE_URL)
  911. versions = disc.version_data()
  912. self.assertEqual(0, len(versions))
  913. versions = disc.version_data(allow_unknown=True)
  914. self.assertEqual(1, len(versions))
  915. self.assertEqual(status, versions[0]['raw_status'])
  916. self.assertEqual(V3_URL, versions[0]['url'])
  917. self.assertEqual((3, 0), versions[0]['version'])
  918. def test_ignoring_invalid_links(self):
  919. version_list = [{'id': 'v3.0',
  920. 'links': [{'href': V3_URL, 'rel': 'self'}],
  921. 'media-types': V3_MEDIA_TYPES,
  922. 'status': 'stable',
  923. 'updated': UPDATED},
  924. {'id': 'v3.1',
  925. 'media-types': V3_MEDIA_TYPES,
  926. 'status': 'stable',
  927. 'updated': UPDATED},
  928. {'media-types': V3_MEDIA_TYPES,
  929. 'status': 'stable',
  930. 'updated': UPDATED,
  931. 'links': [{'href': V3_URL, 'rel': 'self'}],
  932. }]
  933. self.requests_mock.get(BASE_URL, json={'versions': version_list})
  934. disc = discover.Discover(self.session, BASE_URL)
  935. # raw_version_data will return all choices, even invalid ones
  936. versions = disc.raw_version_data()
  937. self.assertEqual(3, len(versions))
  938. # only the version with both id and links will be actually returned
  939. versions = disc.version_data()
  940. self.assertEqual(1, len(versions))
  941. class EndpointDataTests(utils.TestCase):
  942. @mock.patch('keystoneauth1.discover.get_discovery')
  943. @mock.patch('keystoneauth1.discover.EndpointData.'
  944. '_get_discovery_url_choices')
  945. def test_run_discovery_cache(self, mock_url_choices, mock_get_disc):
  946. # get_discovery raises so we keep looping
  947. mock_get_disc.side_effect = exceptions.DiscoveryFailure()
  948. # Duplicate 'url1' in here to validate the cache behavior
  949. mock_url_choices.return_value = ('url1', 'url2', 'url1', 'url3')
  950. epd = discover.EndpointData()
  951. epd._run_discovery(
  952. session='sess', cache='cache', min_version='min',
  953. max_version='max', project_id='projid',
  954. allow_version_hack='allow_hack', discover_versions='disc_vers')
  955. # Only one call with 'url1'
  956. self.assertEqual(3, mock_get_disc.call_count)
  957. mock_get_disc.assert_has_calls(
  958. [mock.call('sess', url, cache='cache', authenticated=False)
  959. for url in ('url1', 'url2', 'url3')])
  960. def test_endpoint_data_str(self):
  961. """Validate EndpointData.__str__."""
  962. # Populate a few fields to make sure they come through.
  963. epd = discover.EndpointData(catalog_url='abc', service_type='123',
  964. api_version=(2, 3))
  965. exp = (
  966. 'EndpointData{api_version=(2, 3), catalog_url=abc,'
  967. ' endpoint_id=None, interface=None, major_version=None,'
  968. ' max_microversion=None, min_microversion=None,'
  969. ' next_min_version=None, not_before=None, raw_endpoint=None,'
  970. ' region_name=None, service_id=None, service_name=None,'
  971. ' service_type=123, service_url=None, url=abc}')
  972. # Works with str()
  973. self.assertEqual(exp, str(epd))
  974. # Works with implicit stringification
  975. self.assertEqual(exp, "%s" % epd)