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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118
  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, out_min, out_max):
  290. self.assertEqual(
  291. (out_min, out_max),
  292. discover._normalize_version_args(in_ver, in_min, in_max))
  293. def normalize_raises(ver, min, max):
  294. self.assertRaises(ValueError,
  295. discover._normalize_version_args, ver, min, max)
  296. assert_min_max(None, None, None,
  297. None, None)
  298. assert_min_max(None, None, 'v1.2',
  299. None, (1, 2))
  300. assert_min_max(None, 'v1.2', 'latest',
  301. (1, 2), (discover.LATEST, discover.LATEST))
  302. assert_min_max(None, 'v1.2', '1.6',
  303. (1, 2), (1, 6))
  304. assert_min_max(None, 'v1.2', '1.latest',
  305. (1, 2), (1, discover.LATEST))
  306. assert_min_max(None, 'latest', 'latest',
  307. (discover.LATEST, discover.LATEST),
  308. (discover.LATEST, discover.LATEST))
  309. assert_min_max(None, 'latest', None,
  310. (discover.LATEST, discover.LATEST),
  311. (discover.LATEST, discover.LATEST))
  312. assert_min_max(None, (1, 2), None,
  313. (1, 2), (discover.LATEST, discover.LATEST))
  314. assert_min_max('', ('1', '2'), (1, 6),
  315. (1, 2), (1, 6))
  316. assert_min_max(None, ('1', '2'), (1, discover.LATEST),
  317. (1, 2), (1, discover.LATEST))
  318. assert_min_max('v1.2', '', None,
  319. (1, 2), (1, discover.LATEST))
  320. assert_min_max('1.latest', None, '',
  321. (1, discover.LATEST), (1, discover.LATEST))
  322. assert_min_max('v1', None, None,
  323. (1, 0), (1, discover.LATEST))
  324. assert_min_max('latest', None, None,
  325. (discover.LATEST, discover.LATEST),
  326. (discover.LATEST, discover.LATEST))
  327. normalize_raises('v1', 'v2', None)
  328. normalize_raises('v1', None, 'v2')
  329. normalize_raises(None, 'latest', 'v1')
  330. normalize_raises(None, 'v1.2', 'v1.1')
  331. normalize_raises(None, 'v1.2', 1)
  332. def test_version_to_string(self):
  333. def assert_string(out, inp):
  334. self.assertEqual(out, discover.version_to_string(inp))
  335. assert_string('latest', (discover.LATEST,))
  336. assert_string('latest', (discover.LATEST, discover.LATEST))
  337. assert_string('latest',
  338. (discover.LATEST, discover.LATEST, discover.LATEST))
  339. assert_string('1', (1,))
  340. assert_string('1.2', (1, 2))
  341. assert_string('1.latest', (1, discover.LATEST))
  342. def test_version_between(self):
  343. def good(minver, maxver, cand):
  344. self.assertTrue(discover._version_between(minver, maxver, cand))
  345. def bad(minver, maxver, cand):
  346. self.assertFalse(discover._version_between(minver, maxver, cand))
  347. def exc(excls, minver, maxver, cand):
  348. self.assertRaises(excls,
  349. discover._version_between, minver, maxver, cand)
  350. # candidate required
  351. exc(ValueError, (1, 0), (1, 0), None)
  352. exc(ValueError, 'v1.0', '1.0', '')
  353. # malformed candidate
  354. exc(TypeError, None, None, 'bogus')
  355. exc(TypeError, None, None, (1, 'two'))
  356. # malformed min_version
  357. exc(TypeError, 'bogus', None, (1, 0))
  358. exc(TypeError, (1, 'two'), None, (1, 0))
  359. # malformed max_version
  360. exc(TypeError, None, 'bogus', (1, 0))
  361. exc(TypeError, None, (1, 'two'), (1, 0))
  362. # fail on minimum
  363. bad((2, 4), None, (1, 55))
  364. bad('v2.4', '', '2.3')
  365. bad('latest', None, (2, 3000))
  366. bad((2, discover.LATEST), '', 'v2.3000')
  367. bad((2, 3000), '', (1, discover.LATEST))
  368. bad('latest', None, 'v1000.latest')
  369. # fail on maximum
  370. bad(None, (2, 4), (2, 5))
  371. bad('', 'v2.4', '2.5')
  372. bad(None, (2, discover.LATEST), (3, 0))
  373. bad('', '2000.latest', 'latest')
  374. # candidate matches a bound
  375. good((1, 0), (1, 0), (1, 0))
  376. good('1.0', '2.9', '1.0')
  377. good('v1.0', 'v2.9', 'v2.9')
  378. # properly in between
  379. good((1, 0), (1, 10), (1, 2))
  380. good('1', '2', '1.2')
  381. # no lower bound
  382. good(None, (2, 5), (2, 3))
  383. # no upper bound
  384. good('2.5', '', '2.6')
  385. # no bounds at all
  386. good('', '', 'v1')
  387. good(None, None, (999, 999))
  388. good(None, None, 'latest')
  389. # Various good 'latest' scenarios
  390. good((discover.LATEST, discover.LATEST),
  391. (discover.LATEST, discover.LATEST),
  392. (discover.LATEST, discover.LATEST))
  393. good((discover.LATEST, discover.LATEST), None,
  394. (discover.LATEST, discover.LATEST))
  395. good('', 'latest', 'latest')
  396. good('2.latest', '3.latest', '3.0')
  397. good('2.latest', None, (55, 66))
  398. good(None, '3.latest', '3.9999')
  399. class VersionDataTests(utils.TestCase):
  400. def setUp(self):
  401. super(VersionDataTests, self).setUp()
  402. self.session = session.Session()
  403. def test_version_data_basics(self):
  404. examples = {'keystone': V3_VERSION_LIST,
  405. 'cinder': CINDER_EXAMPLES,
  406. 'glance': GLANCE_EXAMPLES}
  407. for path, data in examples.items():
  408. url = "%s%s" % (BASE_URL, path)
  409. mock = self.requests_mock.get(url, status_code=300, json=data)
  410. disc = discover.Discover(self.session, url)
  411. raw_data = disc.raw_version_data()
  412. clean_data = disc.version_data()
  413. for v in raw_data:
  414. for n in ('id', 'status', 'links'):
  415. msg = '%s missing from %s version data' % (n, path)
  416. self.assertThat(v, matchers.Annotate(msg,
  417. matchers.Contains(n)))
  418. for v in clean_data:
  419. for n in ('version', 'url', 'raw_status'):
  420. msg = '%s missing from %s version data' % (n, path)
  421. self.assertThat(v, matchers.Annotate(msg,
  422. matchers.Contains(n)))
  423. self.assertTrue(mock.called_once)
  424. def test_version_data_unknown(self):
  425. discovery_fixture = fixture.V3Discovery(V3_URL)
  426. discovery_fixture.status = 'hungry'
  427. discovery_doc = _create_single_version(discovery_fixture)
  428. self.requests_mock.get(V3_URL, status_code=200, json=discovery_doc)
  429. disc = discover.Discover(self.session, V3_URL)
  430. clean_data = disc.version_data(allow_unknown=True)
  431. self.assertEqual(discover.Status.UNKNOWN, clean_data[0]['status'])
  432. def test_version_data_individual(self):
  433. mock = self.requests_mock.get(V3_URL,
  434. status_code=200,
  435. json=V3_VERSION_ENTRY)
  436. disc = discover.Discover(self.session, V3_URL)
  437. raw_data = disc.raw_version_data()
  438. clean_data = disc.version_data()
  439. for v in raw_data:
  440. self.assertEqual(v['id'], 'v3.0')
  441. self.assertEqual(v['status'], 'stable')
  442. self.assertIn('media-types', v)
  443. self.assertIn('links', v)
  444. for v in clean_data:
  445. self.assertEqual(v['version'], (3, 0))
  446. self.assertEqual(v['status'], discover.Status.CURRENT)
  447. self.assertEqual(v['raw_status'], 'stable')
  448. self.assertEqual(v['url'], V3_URL)
  449. self.assertTrue(mock.called_once)
  450. def test_version_data_microversions(self):
  451. """Validate [min_|max_]version conversion to {min|max}_microversion."""
  452. def setup_mock(versions_in):
  453. # Set up the test data with the input version data
  454. jsondata = {
  455. "versions": [
  456. dict(
  457. {
  458. "status": discover.Status.CURRENT,
  459. "id": "v2.2",
  460. "links": [
  461. {
  462. "href": V3_URL,
  463. "rel": "self"
  464. }
  465. ]
  466. },
  467. **versions_in
  468. )
  469. ]
  470. }
  471. self.requests_mock.get(
  472. V3_URL, status_code=200, json=jsondata)
  473. def test_ok(versions_in, versions_out):
  474. setup_mock(versions_in)
  475. # Ensure the output contains the expected microversions
  476. self.assertEqual(
  477. [
  478. dict(
  479. {
  480. 'collection': None,
  481. 'version': (2, 2),
  482. 'url': V3_URL,
  483. 'status': discover.Status.CURRENT,
  484. 'raw_status': discover.Status.CURRENT,
  485. },
  486. **versions_out
  487. )
  488. ],
  489. discover.Discover(self.session, V3_URL).version_data())
  490. def test_exc(versions_in):
  491. setup_mock(versions_in)
  492. # Ensure TypeError is raised
  493. self.assertRaises(
  494. TypeError,
  495. discover.Discover(self.session, V3_URL).version_data)
  496. # no version info in input
  497. test_ok({},
  498. {'min_microversion': None, 'max_microversion': None,
  499. 'next_min_version': None, 'not_before': None})
  500. # version => max_microversion
  501. test_ok({'version': '2.2'},
  502. {'min_microversion': None, 'max_microversion': (2, 2),
  503. 'next_min_version': None, 'not_before': None})
  504. # max_version supersedes version (even if malformed). min_version &
  505. # normalization.
  506. test_ok({'min_version': '2', 'version': 'foo', 'max_version': '2.2'},
  507. {'min_microversion': (2, 0), 'max_microversion': (2, 2),
  508. 'next_min_version': None, 'not_before': None})
  509. # Edge case: min/max_version ignored if present but "empty"; version
  510. # used for max_microversion.
  511. test_ok({'min_version': '', 'version': '2.1', 'max_version': ''},
  512. {'min_microversion': None, 'max_microversion': (2, 1),
  513. 'next_min_version': None, 'not_before': None})
  514. # next_min_version set
  515. test_ok({'min_version': '2', 'max_version': '2.2',
  516. 'next_min_version': '2.1', 'not_before': '2019-07-01'},
  517. {'min_microversion': (2, 0), 'max_microversion': (2, 2),
  518. 'next_min_version': (2, 1), 'not_before': '2019-07-01'})
  519. # Badly-formatted min_version
  520. test_exc({'min_version': 'foo', 'max_version': '2.1'})
  521. # Badly-formatted max_version
  522. test_exc({'min_version': '2.1', 'max_version': 'foo'})
  523. # Badly-formatted version (when max_version omitted)
  524. test_exc({'min_version': '2.1', 'version': 'foo'})
  525. # Badly-formatted next_min_version
  526. test_exc({'next_min_version': 'bogus', 'not_before': '2019-07-01'})
  527. def test_endpoint_data_noauth_discover(self):
  528. mock = self.requests_mock.get(
  529. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  530. plugin = noauth.NoAuth()
  531. data = plugin.get_endpoint_data(self.session, endpoint_override=V3_URL)
  532. self.assertEqual(data.api_version, (3, 0))
  533. self.assertEqual(data.url, V3_URL)
  534. self.assertEqual(
  535. plugin.get_api_major_version(
  536. self.session, endpoint_override=V3_URL),
  537. (3, 0))
  538. self.assertEqual(
  539. plugin.get_endpoint(self.session, endpoint_override=V3_URL),
  540. V3_URL)
  541. self.assertTrue(mock.called_once)
  542. def test_endpoint_data_noauth_no_discover(self):
  543. plugin = noauth.NoAuth()
  544. data = plugin.get_endpoint_data(
  545. self.session, endpoint_override=V3_URL, discover_versions=False)
  546. self.assertEqual(data.api_version, (3, 0))
  547. self.assertEqual(data.url, V3_URL)
  548. self.assertEqual(
  549. plugin.get_api_major_version(
  550. self.session, endpoint_override=V3_URL),
  551. (3, 0))
  552. self.assertEqual(
  553. plugin.get_endpoint(self.session, endpoint_override=V3_URL),
  554. V3_URL)
  555. def test_endpoint_data_noauth_adapter(self):
  556. mock = self.requests_mock.get(
  557. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  558. client = adapter.Adapter(
  559. session.Session(noauth.NoAuth()),
  560. endpoint_override=V3_URL)
  561. data = client.get_endpoint_data()
  562. self.assertEqual(data.api_version, (3, 0))
  563. self.assertEqual(data.url, V3_URL)
  564. self.assertEqual(client.get_api_major_version(), (3, 0))
  565. self.assertEqual(client.get_endpoint(), V3_URL)
  566. self.assertTrue(mock.called_once)
  567. def test_endpoint_data_token_endpoint_discover(self):
  568. mock = self.requests_mock.get(
  569. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  570. plugin = token_endpoint.Token(endpoint=V3_URL, token='bogus')
  571. data = plugin.get_endpoint_data(self.session)
  572. self.assertEqual(data.api_version, (3, 0))
  573. self.assertEqual(data.url, V3_URL)
  574. self.assertEqual(plugin.get_api_major_version(self.session), (3, 0))
  575. self.assertEqual(plugin.get_endpoint(self.session), V3_URL)
  576. self.assertTrue(mock.called_once)
  577. def test_endpoint_data_token_endpoint_no_discover(self):
  578. plugin = token_endpoint.Token(endpoint=V3_URL, token='bogus')
  579. data = plugin.get_endpoint_data(self.session, discover_versions=False)
  580. self.assertEqual(data.api_version, (3, 0))
  581. self.assertEqual(data.url, V3_URL)
  582. self.assertEqual(plugin.get_api_major_version(self.session), (3, 0))
  583. self.assertEqual(plugin.get_endpoint(self.session), V3_URL)
  584. def test_endpoint_data_token_endpoint_adapter(self):
  585. mock = self.requests_mock.get(
  586. V3_URL, status_code=200, json=V3_VERSION_ENTRY)
  587. plugin = token_endpoint.Token(endpoint=V3_URL, token='bogus')
  588. client = adapter.Adapter(session.Session(plugin))
  589. data = client.get_endpoint_data()
  590. self.assertEqual(data.api_version, (3, 0))
  591. self.assertEqual(data.url, V3_URL)
  592. self.assertEqual(client.get_api_major_version(), (3, 0))
  593. self.assertEqual(client.get_endpoint(), V3_URL)
  594. self.assertTrue(mock.called_once)
  595. def test_data_for_url(self):
  596. mock = self.requests_mock.get(V3_URL,
  597. status_code=200,
  598. json=V3_VERSION_ENTRY)
  599. disc = discover.Discover(self.session, V3_URL)
  600. for url in (V3_URL, V3_URL + '/'):
  601. data = disc.versioned_data_for(url=url)
  602. self.assertEqual(data['version'], (3, 0))
  603. self.assertEqual(data['raw_status'], 'stable')
  604. self.assertEqual(data['url'], V3_URL)
  605. self.assertTrue(mock.called_once)
  606. def test_data_for_no_version(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. data = disc.versioned_data_for()
  612. self.assertEqual(data['version'], (3, 0))
  613. self.assertEqual(data['raw_status'], 'stable')
  614. self.assertEqual(data['url'], V3_URL)
  615. self.assertRaises(TypeError, disc.data_for, version=None)
  616. self.assertTrue(mock.called_once)
  617. def test_keystone_version_data(self):
  618. mock = self.requests_mock.get(BASE_URL,
  619. status_code=300,
  620. json=V3_VERSION_LIST)
  621. disc = discover.Discover(self.session, BASE_URL)
  622. raw_data = disc.raw_version_data()
  623. clean_data = disc.version_data()
  624. self.assertEqual(2, len(raw_data))
  625. self.assertEqual(2, len(clean_data))
  626. for v in raw_data:
  627. self.assertIn(v['id'], ('v2.0', 'v3.0'))
  628. self.assertEqual(v['updated'], UPDATED)
  629. self.assertEqual(v['status'], 'stable')
  630. if v['id'] == 'v3.0':
  631. self.assertEqual(v['media-types'], V3_MEDIA_TYPES)
  632. for v in clean_data:
  633. self.assertIn(v['version'], ((2, 0), (3, 0)))
  634. self.assertEqual(v['raw_status'], 'stable')
  635. valid_v3_versions = (
  636. disc.data_for('v3.0'),
  637. disc.data_for('3.latest'),
  638. disc.data_for('latest'),
  639. disc.versioned_data_for(min_version='v3.0',
  640. max_version='v3.latest'),
  641. disc.versioned_data_for(min_version='3'),
  642. disc.versioned_data_for(min_version='3.latest'),
  643. disc.versioned_data_for(min_version='latest'),
  644. disc.versioned_data_for(min_version='3.latest',
  645. max_version='latest'),
  646. disc.versioned_data_for(min_version='latest',
  647. max_version='latest'),
  648. disc.versioned_data_for(min_version=2),
  649. disc.versioned_data_for(min_version='2.latest'))
  650. for version in valid_v3_versions:
  651. self.assertEqual((3, 0), version['version'])
  652. self.assertEqual('stable', version['raw_status'])
  653. self.assertEqual(V3_URL, version['url'])
  654. valid_v2_versions = (
  655. disc.data_for(2),
  656. disc.data_for('2.latest'),
  657. disc.versioned_data_for(min_version=2,
  658. max_version=(2, discover.LATEST)),
  659. disc.versioned_data_for(min_version='2.latest',
  660. max_version='2.latest'))
  661. for version in valid_v2_versions:
  662. self.assertEqual((2, 0), version['version'])
  663. self.assertEqual('stable', version['raw_status'])
  664. self.assertEqual(V2_URL, version['url'])
  665. self.assertIsNone(disc.url_for('v4'))
  666. self.assertIsNone(disc.versioned_url_for(
  667. min_version='v4', max_version='v4.latest'))
  668. self.assertEqual(V3_URL, disc.url_for('v3'))
  669. self.assertEqual(V3_URL, disc.versioned_url_for(
  670. min_version='v3', max_version='v3.latest'))
  671. self.assertEqual(V2_URL, disc.url_for('v2'))
  672. self.assertEqual(V2_URL, disc.versioned_url_for(
  673. min_version='v2', max_version='v2.latest'))
  674. self.assertTrue(mock.called_once)
  675. def test_cinder_version_data(self):
  676. mock = self.requests_mock.get(BASE_URL,
  677. status_code=300,
  678. json=CINDER_EXAMPLES)
  679. disc = discover.Discover(self.session, BASE_URL)
  680. raw_data = disc.raw_version_data()
  681. clean_data = disc.version_data()
  682. self.assertEqual(3, len(raw_data))
  683. for v in raw_data:
  684. self.assertEqual(v['status'], discover.Status.CURRENT)
  685. if v['id'] == 'v1.0':
  686. self.assertEqual(v['updated'], '2012-01-04T11:33:21Z')
  687. elif v['id'] == 'v2.0':
  688. self.assertEqual(v['updated'], '2012-11-21T11:33:21Z')
  689. elif v['id'] == 'v3.0':
  690. self.assertEqual(v['updated'], '2012-11-21T11:33:21Z')
  691. else:
  692. self.fail("Invalid version found")
  693. v1_url = "%sv1/" % BASE_URL
  694. v2_url = "%sv2/" % BASE_URL
  695. v3_url = "%sv3/" % BASE_URL
  696. self.assertEqual(clean_data, [
  697. {
  698. 'collection': None,
  699. 'max_microversion': None,
  700. 'min_microversion': None,
  701. 'next_min_version': None,
  702. 'not_before': None,
  703. 'version': (1, 0),
  704. 'url': v1_url,
  705. 'status': discover.Status.CURRENT,
  706. 'raw_status': discover.Status.CURRENT,
  707. },
  708. {
  709. 'collection': None,
  710. 'max_microversion': None,
  711. 'min_microversion': None,
  712. 'next_min_version': None,
  713. 'not_before': None,
  714. 'version': (2, 0),
  715. 'url': v2_url,
  716. 'status': discover.Status.CURRENT,
  717. 'raw_status': discover.Status.CURRENT,
  718. },
  719. {
  720. 'collection': BASE_URL,
  721. 'max_microversion': (3, 27),
  722. 'min_microversion': (3, 0),
  723. 'next_min_version': (3, 4),
  724. 'not_before': u'2019-12-31',
  725. 'version': (3, 0),
  726. 'url': v3_url,
  727. 'status': discover.Status.CURRENT,
  728. 'raw_status': discover.Status.CURRENT,
  729. },
  730. ])
  731. for version in (disc.data_for('v2.0'),
  732. disc.versioned_data_for(min_version='v2.0',
  733. max_version='v2.latest')):
  734. self.assertEqual((2, 0), version['version'])
  735. self.assertEqual(discover.Status.CURRENT, version['raw_status'])
  736. self.assertEqual(v2_url, version['url'])
  737. for version in (disc.data_for(1),
  738. disc.versioned_data_for(
  739. min_version=(1,),
  740. max_version=(1, discover.LATEST))):
  741. self.assertEqual((1, 0), version['version'])
  742. self.assertEqual(discover.Status.CURRENT, version['raw_status'])
  743. self.assertEqual(v1_url, version['url'])
  744. self.assertIsNone(disc.url_for('v4'))
  745. self.assertIsNone(disc.versioned_url_for(min_version='v4',
  746. max_version='v4.latest'))
  747. self.assertEqual(v3_url, disc.url_for('v3'))
  748. self.assertEqual(v3_url, disc.versioned_url_for(
  749. min_version='v3', max_version='v3.latest'))
  750. self.assertEqual(v2_url, disc.url_for('v2'))
  751. self.assertEqual(v2_url, disc.versioned_url_for(
  752. min_version='v2', max_version='v2.latest'))
  753. self.assertEqual(v1_url, disc.url_for('v1'))
  754. self.assertEqual(v1_url, disc.versioned_url_for(
  755. min_version='v1', max_version='v1.latest'))
  756. self.assertTrue(mock.called_once)
  757. def test_glance_version_data(self):
  758. mock = self.requests_mock.get(BASE_URL,
  759. status_code=200,
  760. json=GLANCE_EXAMPLES)
  761. disc = discover.Discover(self.session, BASE_URL)
  762. raw_data = disc.raw_version_data()
  763. clean_data = disc.version_data()
  764. self.assertEqual(5, len(raw_data))
  765. for v in raw_data:
  766. if v['id'] in ('v2.2', 'v1.1'):
  767. self.assertEqual(v['status'], discover.Status.CURRENT)
  768. elif v['id'] in ('v2.1', 'v2.0', 'v1.0'):
  769. self.assertEqual(v['status'], discover.Status.SUPPORTED)
  770. else:
  771. self.fail("Invalid version found")
  772. v1_url = '%sv1/' % BASE_URL
  773. v2_url = '%sv2/' % BASE_URL
  774. self.assertEqual(clean_data, [
  775. {
  776. 'collection': None,
  777. 'max_microversion': None,
  778. 'min_microversion': None,
  779. 'next_min_version': None,
  780. 'not_before': None,
  781. 'version': (1, 0),
  782. 'url': v1_url,
  783. 'status': discover.Status.SUPPORTED,
  784. 'raw_status': discover.Status.SUPPORTED,
  785. },
  786. {
  787. 'collection': None,
  788. 'max_microversion': None,
  789. 'min_microversion': None,
  790. 'next_min_version': None,
  791. 'not_before': None,
  792. 'version': (1, 1),
  793. 'url': v1_url,
  794. 'status': discover.Status.CURRENT,
  795. 'raw_status': discover.Status.CURRENT,
  796. },
  797. {
  798. 'collection': None,
  799. 'max_microversion': None,
  800. 'min_microversion': None,
  801. 'next_min_version': None,
  802. 'not_before': None,
  803. 'version': (2, 0),
  804. 'url': v2_url,
  805. 'status': discover.Status.SUPPORTED,
  806. 'raw_status': discover.Status.SUPPORTED,
  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, 1),
  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, 2),
  826. 'url': v2_url,
  827. 'status': discover.Status.CURRENT,
  828. 'raw_status': discover.Status.CURRENT,
  829. },
  830. ])
  831. for ver in (2, 2.1, 2.2):
  832. for version in (disc.data_for(ver),
  833. disc.versioned_data_for(
  834. min_version=ver,
  835. max_version=(2, discover.LATEST))):
  836. self.assertEqual((2, 2), version['version'])
  837. self.assertEqual(
  838. discover.Status.CURRENT, version['raw_status']
  839. )
  840. self.assertEqual(v2_url, version['url'])
  841. self.assertEqual(v2_url, disc.url_for(ver))
  842. for ver in (1, 1.1):
  843. for version in (disc.data_for(ver),
  844. disc.versioned_data_for(
  845. min_version=ver,
  846. max_version=(1, discover.LATEST))):
  847. self.assertEqual((1, 1), version['version'])
  848. self.assertEqual(
  849. discover.Status.CURRENT, version['raw_status']
  850. )
  851. self.assertEqual(v1_url, version['url'])
  852. self.assertEqual(v1_url, disc.url_for(ver))
  853. self.assertIsNone(disc.url_for('v3'))
  854. self.assertIsNone(disc.versioned_url_for(min_version='v3',
  855. max_version='v3.latest'))
  856. self.assertIsNone(disc.url_for('v2.3'))
  857. self.assertIsNone(disc.versioned_url_for(min_version='v2.3',
  858. max_version='v2.latest'))
  859. self.assertTrue(mock.called_once)
  860. def test_allow_deprecated(self):
  861. status = 'deprecated'
  862. version_list = [{'id': 'v3.0',
  863. 'links': [{'href': V3_URL, 'rel': 'self'}],
  864. 'media-types': V3_MEDIA_TYPES,
  865. 'status': status,
  866. 'updated': UPDATED}]
  867. self.requests_mock.get(BASE_URL, json={'versions': version_list})
  868. disc = discover.Discover(self.session, BASE_URL)
  869. # deprecated is allowed by default
  870. versions = disc.version_data(allow_deprecated=False)
  871. self.assertEqual(0, len(versions))
  872. versions = disc.version_data(allow_deprecated=True)
  873. self.assertEqual(1, len(versions))
  874. self.assertEqual(status, versions[0]['raw_status'])
  875. self.assertEqual(V3_URL, versions[0]['url'])
  876. self.assertEqual((3, 0), versions[0]['version'])
  877. def test_allow_experimental(self):
  878. status = 'experimental'
  879. version_list = [{'id': 'v3.0',
  880. 'links': [{'href': V3_URL, 'rel': 'self'}],
  881. 'media-types': V3_MEDIA_TYPES,
  882. 'status': status,
  883. 'updated': UPDATED}]
  884. self.requests_mock.get(BASE_URL, json={'versions': version_list})
  885. disc = discover.Discover(self.session, BASE_URL)
  886. versions = disc.version_data()
  887. self.assertEqual(0, len(versions))
  888. versions = disc.version_data(allow_experimental=True)
  889. self.assertEqual(1, len(versions))
  890. self.assertEqual(status, versions[0]['raw_status'])
  891. self.assertEqual(V3_URL, versions[0]['url'])
  892. self.assertEqual((3, 0), versions[0]['version'])
  893. def test_allow_unknown(self):
  894. status = 'abcdef'
  895. version_list = fixture.DiscoveryList(BASE_URL,
  896. v2=False,
  897. v3_status=status)
  898. self.requests_mock.get(BASE_URL, json=version_list)
  899. disc = discover.Discover(self.session, BASE_URL)
  900. versions = disc.version_data()
  901. self.assertEqual(0, len(versions))
  902. versions = disc.version_data(allow_unknown=True)
  903. self.assertEqual(1, len(versions))
  904. self.assertEqual(status, versions[0]['raw_status'])
  905. self.assertEqual(V3_URL, versions[0]['url'])
  906. self.assertEqual((3, 0), versions[0]['version'])
  907. def test_ignoring_invalid_links(self):
  908. version_list = [{'id': 'v3.0',
  909. 'links': [{'href': V3_URL, 'rel': 'self'}],
  910. 'media-types': V3_MEDIA_TYPES,
  911. 'status': 'stable',
  912. 'updated': UPDATED},
  913. {'id': 'v3.1',
  914. 'media-types': V3_MEDIA_TYPES,
  915. 'status': 'stable',
  916. 'updated': UPDATED},
  917. {'media-types': V3_MEDIA_TYPES,
  918. 'status': 'stable',
  919. 'updated': UPDATED,
  920. 'links': [{'href': V3_URL, 'rel': 'self'}],
  921. }]
  922. self.requests_mock.get(BASE_URL, json={'versions': version_list})
  923. disc = discover.Discover(self.session, BASE_URL)
  924. # raw_version_data will return all choices, even invalid ones
  925. versions = disc.raw_version_data()
  926. self.assertEqual(3, len(versions))
  927. # only the version with both id and links will be actually returned
  928. versions = disc.version_data()
  929. self.assertEqual(1, len(versions))
  930. class EndpointDataTests(utils.TestCase):
  931. @mock.patch('keystoneauth1.discover.get_discovery')
  932. @mock.patch('keystoneauth1.discover.EndpointData.'
  933. '_get_discovery_url_choices')
  934. def test_run_discovery_cache(self, mock_url_choices, mock_get_disc):
  935. # get_discovery raises so we keep looping
  936. mock_get_disc.side_effect = exceptions.DiscoveryFailure()
  937. # Duplicate 'url1' in here to validate the cache behavior
  938. mock_url_choices.return_value = ('url1', 'url2', 'url1', 'url3')
  939. epd = discover.EndpointData()
  940. epd._run_discovery(
  941. session='sess', cache='cache', min_version='min',
  942. max_version='max', project_id='projid',
  943. allow_version_hack='allow_hack', discover_versions='disc_vers')
  944. # Only one call with 'url1'
  945. self.assertEqual(3, mock_get_disc.call_count)
  946. mock_get_disc.assert_has_calls(
  947. [mock.call('sess', url, cache='cache', authenticated=False)
  948. for url in ('url1', 'url2', 'url3')])
  949. def test_endpoint_data_str(self):
  950. """Validate EndpointData.__str__."""
  951. # Populate a few fields to make sure they come through.
  952. epd = discover.EndpointData(catalog_url='abc', service_type='123',
  953. api_version=(2, 3))
  954. exp = (
  955. 'EndpointData{api_version=(2, 3), catalog_url=abc,'
  956. ' endpoint_id=None, interface=None, major_version=None,'
  957. ' max_microversion=None, min_microversion=None,'
  958. ' next_min_version=None, not_before=None, raw_endpoint=None,'
  959. ' region_name=None, service_id=None, service_name=None,'
  960. ' service_type=123, service_url=None, url=abc}')
  961. # Works with str()
  962. self.assertEqual(exp, str(epd))
  963. # Works with implicit stringification
  964. self.assertEqual(exp, "%s" % epd)