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_identity_common.py 86KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267
  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 abc
  13. import collections
  14. import uuid
  15. import six
  16. from six.moves import urllib
  17. from keystoneauth1 import _utils
  18. from keystoneauth1 import access
  19. from keystoneauth1 import discover
  20. from keystoneauth1 import exceptions
  21. from keystoneauth1 import fixture
  22. from keystoneauth1 import identity
  23. from keystoneauth1 import plugin
  24. from keystoneauth1 import session
  25. from keystoneauth1.tests.unit import utils
  26. _Endpoints = collections.namedtuple(
  27. 'ServiceVersion',
  28. 'public, internal, admin')
  29. _ServiceVersion = collections.namedtuple(
  30. 'ServiceVersion',
  31. 'discovery, service')
  32. class FakeServiceEndpoints(object):
  33. def __init__(self, base_url, versions=None, project_id=None, **kwargs):
  34. self.base_url = base_url
  35. self._interfaces = {}
  36. for interface in ('public', 'internal', 'admin'):
  37. if interface in kwargs and not kwargs[interface]:
  38. self._interfaces[interface] = False
  39. else:
  40. self._interfaces[interface] = True
  41. self.versions = {}
  42. self.unversioned = self._make_urls()
  43. if not versions:
  44. self.catalog = self.unversioned
  45. else:
  46. self.catalog = self._make_urls(versions[0], project_id)
  47. for version in versions:
  48. self.versions[version] = _ServiceVersion(
  49. self._make_urls(version),
  50. self._make_urls(version, project_id),
  51. )
  52. def _make_urls(self, *parts):
  53. return _Endpoints(
  54. self._make_url('public', *parts),
  55. self._make_url('internal', *parts),
  56. self._make_url('admin', *parts),
  57. )
  58. def _make_url(self, interface, *parts):
  59. if not self._interfaces[interface]:
  60. return None
  61. url = urllib.parse.urljoin(self.base_url + '/', interface)
  62. for part in parts:
  63. if part:
  64. url = urllib.parse.urljoin(url + '/', part)
  65. return url
  66. @six.add_metaclass(abc.ABCMeta)
  67. class CommonIdentityTests(object):
  68. PROJECT_ID = uuid.uuid4().hex
  69. TEST_ROOT_URL = 'http://127.0.0.1:5000/'
  70. TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/'
  71. TEST_COMPUTE_BASE = 'https://compute.example.com'
  72. TEST_COMPUTE_PUBLIC = TEST_COMPUTE_BASE + '/nova/public'
  73. TEST_COMPUTE_INTERNAL = TEST_COMPUTE_BASE + '/nova/internal'
  74. TEST_COMPUTE_ADMIN = TEST_COMPUTE_BASE + '/nova/admin'
  75. TEST_VOLUME = FakeServiceEndpoints(
  76. base_url='https://block-storage.example.com',
  77. versions=['v3', 'v2'], project_id=PROJECT_ID)
  78. TEST_BAREMETAL_BASE = 'https://baremetal.example.com'
  79. TEST_BAREMETAL_INTERNAL = TEST_BAREMETAL_BASE + '/internal'
  80. TEST_PASS = uuid.uuid4().hex
  81. def setUp(self):
  82. super(CommonIdentityTests, self).setUp()
  83. self.TEST_URL = '%s%s' % (self.TEST_ROOT_URL, self.version)
  84. self.TEST_ADMIN_URL = '%s%s' % (self.TEST_ROOT_ADMIN_URL, self.version)
  85. self.TEST_DISCOVERY = fixture.DiscoveryList(href=self.TEST_ROOT_URL)
  86. self.stub_auth_data()
  87. @abc.abstractmethod
  88. def create_auth_plugin(self, **kwargs):
  89. """Create an auth plugin that makes sense for the auth data.
  90. It doesn't really matter what auth mechanism is used but it should be
  91. appropriate to the API version.
  92. """
  93. @abc.abstractmethod
  94. def get_auth_data(self, **kwargs):
  95. """Return fake authentication data.
  96. This should register a valid token response and ensure that the compute
  97. endpoints are set to TEST_COMPUTE_PUBLIC, _INTERNAL and _ADMIN.
  98. """
  99. def stub_auth_data(self, **kwargs):
  100. token = self.get_auth_data(**kwargs)
  101. self.user_id = token.user_id
  102. try:
  103. self.project_id = token.project_id
  104. except AttributeError:
  105. self.project_id = token.tenant_id
  106. self.stub_auth(json=token)
  107. @abc.abstractproperty
  108. def version(self):
  109. """The API version being tested."""
  110. def test_discovering(self):
  111. disc = fixture.DiscoveryList(v2=False, v3=False)
  112. disc.add_nova_microversion(
  113. href=self.TEST_COMPUTE_ADMIN,
  114. id='v2.1', status='CURRENT',
  115. min_version='2.1', version='2.38')
  116. self.stub_url('GET', [],
  117. base_url=self.TEST_COMPUTE_ADMIN,
  118. json=disc)
  119. body = 'SUCCESS'
  120. # which gives our sample values
  121. self.stub_url('GET', ['path'],
  122. text=body, base_url=self.TEST_COMPUTE_ADMIN)
  123. a = self.create_auth_plugin()
  124. s = session.Session(auth=a)
  125. resp = s.get('/path', endpoint_filter={'service_type': 'compute',
  126. 'interface': 'admin',
  127. 'version': '2.1'})
  128. self.assertEqual(200, resp.status_code)
  129. self.assertEqual(body, resp.text)
  130. new_body = 'SC SUCCESS'
  131. # if we don't specify a version, we use the URL from the SC
  132. self.stub_url('GET', ['path'],
  133. base_url=self.TEST_COMPUTE_ADMIN,
  134. text=new_body)
  135. resp = s.get('/path', endpoint_filter={'service_type': 'compute',
  136. 'interface': 'admin'})
  137. self.assertEqual(200, resp.status_code)
  138. self.assertEqual(new_body, resp.text)
  139. def test_discovery_uses_provided_session_cache(self):
  140. disc = fixture.DiscoveryList(v2=False, v3=False)
  141. disc.add_nova_microversion(
  142. href=self.TEST_COMPUTE_ADMIN,
  143. id='v2.1', status='CURRENT',
  144. min_version='2.1', version='2.38')
  145. # register responses such that if the discovery URL is hit more than
  146. # once then the response will be invalid and not point to COMPUTE_ADMIN
  147. resps = [{'json': disc}, {'status_code': 500}]
  148. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  149. body = 'SUCCESS'
  150. self.stub_url('GET', ['path'],
  151. text=body, base_url=self.TEST_COMPUTE_ADMIN)
  152. cache = {}
  153. # now either of the two plugins I use, it should not cause a second
  154. # request to the discovery url.
  155. s = session.Session(discovery_cache=cache)
  156. a = self.create_auth_plugin()
  157. b = self.create_auth_plugin()
  158. for auth in (a, b):
  159. resp = s.get('/path',
  160. auth=auth,
  161. endpoint_filter={'service_type': 'compute',
  162. 'interface': 'admin',
  163. 'version': '2.1'})
  164. self.assertEqual(200, resp.status_code)
  165. self.assertEqual(body, resp.text)
  166. self.assertIn(self.TEST_COMPUTE_ADMIN, cache.keys())
  167. def test_discovery_uses_session_cache(self):
  168. disc = fixture.DiscoveryList(v2=False, v3=False)
  169. disc.add_nova_microversion(
  170. href=self.TEST_COMPUTE_ADMIN,
  171. id='v2.1', status='CURRENT',
  172. min_version='2.1', version='2.38')
  173. # register responses such that if the discovery URL is hit more than
  174. # once then the response will be invalid and not point to COMPUTE_ADMIN
  175. resps = [{'json': disc}, {'status_code': 500}]
  176. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  177. body = 'SUCCESS'
  178. self.stub_url('GET', ['path'],
  179. base_url=self.TEST_COMPUTE_ADMIN,
  180. text=body)
  181. filter = {'service_type': 'compute', 'interface': 'admin',
  182. 'version': '2.1'}
  183. # create a session and call the endpoint, causing its cache to be set
  184. sess = session.Session()
  185. sess.get('/path', auth=self.create_auth_plugin(),
  186. endpoint_filter=filter)
  187. self.assertIn(self.TEST_COMPUTE_ADMIN, sess._discovery_cache.keys())
  188. # now either of the two plugins I use, it should not cause a second
  189. # request to the discovery url.
  190. a = self.create_auth_plugin()
  191. b = self.create_auth_plugin()
  192. for auth in (a, b):
  193. resp = sess.get('/path', auth=auth, endpoint_filter=filter)
  194. self.assertEqual(200, resp.status_code)
  195. self.assertEqual(body, resp.text)
  196. def test_discovery_uses_plugin_cache(self):
  197. disc = fixture.DiscoveryList(v2=False, v3=False)
  198. disc.add_nova_microversion(
  199. href=self.TEST_COMPUTE_ADMIN,
  200. id='v2.1', status='CURRENT',
  201. min_version='2.1', version='2.38')
  202. # register responses such that if the discovery URL is hit more than
  203. # once then the response will be invalid and not point to COMPUTE_ADMIN
  204. resps = [{'json': disc}, {'status_code': 500}]
  205. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  206. body = 'SUCCESS'
  207. self.stub_url('GET', ['path'],
  208. base_url=self.TEST_COMPUTE_ADMIN,
  209. text=body)
  210. # now either of the two sessions I use, it should not cause a second
  211. # request to the discovery url. Calling discovery directly should also
  212. # not cause an additional request.
  213. sa = session.Session()
  214. sb = session.Session()
  215. auth = self.create_auth_plugin()
  216. for sess in (sa, sb):
  217. resp = sess.get('/path',
  218. auth=auth,
  219. endpoint_filter={'service_type': 'compute',
  220. 'interface': 'admin',
  221. 'version': '2.1'})
  222. self.assertEqual(200, resp.status_code)
  223. self.assertEqual(body, resp.text)
  224. def test_discovery_uses_session_plugin_cache(self):
  225. disc = fixture.DiscoveryList(v2=False, v3=False)
  226. disc.add_nova_microversion(
  227. href=self.TEST_COMPUTE_ADMIN,
  228. id='v2.1', status='CURRENT',
  229. min_version='2.1', version='2.38')
  230. # register responses such that if the discovery URL is hit more than
  231. # once then the response will be invalid and not point to COMPUTE_ADMIN
  232. resps = [{'json': disc}, {'status_code': 500}]
  233. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  234. body = 'SUCCESS'
  235. self.stub_url('GET', ['path'],
  236. base_url=self.TEST_COMPUTE_ADMIN,
  237. text=body)
  238. filter = {'service_type': 'compute', 'interface': 'admin',
  239. 'version': '2.1'}
  240. # create a plugin and call the endpoint, causing its cache to be set
  241. plugin = self.create_auth_plugin()
  242. session.Session().get('/path', auth=plugin, endpoint_filter=filter)
  243. self.assertIn(self.TEST_COMPUTE_ADMIN, plugin._discovery_cache.keys())
  244. # with the plugin in the session, no more calls to the discovery URL
  245. sess = session.Session(auth=plugin)
  246. for auth in (plugin, self.create_auth_plugin()):
  247. resp = sess.get('/path', auth=auth, endpoint_filter=filter)
  248. self.assertEqual(200, resp.status_code)
  249. self.assertEqual(body, resp.text)
  250. def test_direct_discovery_provided_plugin_cache(self):
  251. # register responses such that if the discovery URL is hit more than
  252. # once then the response will be invalid and not point to COMPUTE_ADMIN
  253. resps = [{'json': self.TEST_DISCOVERY}, {'status_code': 500}]
  254. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  255. # now either of the two sessions I use, it should not cause a second
  256. # request to the discovery url. Calling discovery directly should also
  257. # not cause an additional request.
  258. sa = session.Session()
  259. sb = session.Session()
  260. discovery_cache = {}
  261. expected_url = urllib.parse.urljoin(self.TEST_COMPUTE_ADMIN, '/v2.0')
  262. for sess in (sa, sb):
  263. disc = discover.get_discovery(
  264. sess, self.TEST_COMPUTE_ADMIN, cache=discovery_cache)
  265. url = disc.url_for(('2', '0'))
  266. self.assertEqual(expected_url, url)
  267. self.assertIn(self.TEST_COMPUTE_ADMIN, discovery_cache.keys())
  268. def test_discovering_with_no_data(self):
  269. # which returns discovery information pointing to TEST_URL but there is
  270. # no data there.
  271. self.stub_url('GET', [],
  272. base_url=self.TEST_COMPUTE_ADMIN,
  273. status_code=400)
  274. # so the url that will be used is the same TEST_COMPUTE_ADMIN
  275. body = 'SUCCESS'
  276. self.stub_url('GET', ['path'], base_url=self.TEST_COMPUTE_ADMIN,
  277. text=body, status_code=200)
  278. a = self.create_auth_plugin()
  279. s = session.Session(auth=a)
  280. resp = s.get('/path', endpoint_filter={'service_type': 'compute',
  281. 'interface': 'admin',
  282. 'version': self.version})
  283. self.assertEqual(200, resp.status_code)
  284. self.assertEqual(body, resp.text)
  285. def test_direct_discovering_with_no_data(self):
  286. # returns discovery information pointing to TEST_URL but there is
  287. # no data there.
  288. self.stub_url('GET', [],
  289. base_url=self.TEST_COMPUTE_ADMIN,
  290. status_code=400)
  291. a = self.create_auth_plugin()
  292. s = session.Session(auth=a)
  293. # A direct call for discovery should fail
  294. self.assertRaises(exceptions.BadRequest,
  295. discover.get_discovery, s, self.TEST_COMPUTE_ADMIN)
  296. def test_discovering_with_relative_link(self):
  297. # need to construct list this way for relative
  298. disc = fixture.DiscoveryList(v2=False, v3=False)
  299. disc.add_v2('v2.0')
  300. disc.add_v3('v3')
  301. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  302. a = self.create_auth_plugin()
  303. s = session.Session(auth=a)
  304. endpoint_v2 = s.get_endpoint(service_type='compute',
  305. interface='admin',
  306. version=(2, 0))
  307. endpoint_v3 = s.get_endpoint(service_type='compute',
  308. interface='admin',
  309. version=(3, 0))
  310. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v2.0', endpoint_v2)
  311. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v3', endpoint_v3)
  312. def test_direct_discovering(self):
  313. v2_compute = self.TEST_COMPUTE_ADMIN + '/v2.0'
  314. v3_compute = self.TEST_COMPUTE_ADMIN + '/v3'
  315. disc = fixture.DiscoveryList(v2=False, v3=False)
  316. disc.add_v2(v2_compute)
  317. disc.add_v3(v3_compute)
  318. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  319. a = self.create_auth_plugin()
  320. s = session.Session(auth=a)
  321. catalog_url = s.get_endpoint(
  322. service_type='compute', interface='admin')
  323. disc = discover.get_discovery(s, catalog_url)
  324. url_v2 = disc.url_for(('2', '0'))
  325. url_v3 = disc.url_for(('3', '0'))
  326. self.assertEqual(v2_compute, url_v2)
  327. self.assertEqual(v3_compute, url_v3)
  328. # Verify that passing strings and not tuples works
  329. url_v2 = disc.url_for('2.0')
  330. url_v3 = disc.url_for('3.0')
  331. self.assertEqual(v2_compute, url_v2)
  332. self.assertEqual(v3_compute, url_v3)
  333. def test_discovering_version_no_discovery(self):
  334. a = self.create_auth_plugin()
  335. s = session.Session(auth=a)
  336. # Grab a version that can be returned without doing discovery
  337. # This tests that it doesn't make a discovery call because we don't
  338. # have a reqquest mock, and this will throw an exception if it tries
  339. version = s.get_api_major_version(
  340. service_type='volumev2', interface='admin')
  341. self.assertEqual((2, 0), version)
  342. def test_discovering_version_with_discovery(self):
  343. a = self.create_auth_plugin()
  344. s = session.Session(auth=a)
  345. v2_compute = self.TEST_COMPUTE_ADMIN + '/v2.0'
  346. v3_compute = self.TEST_COMPUTE_ADMIN + '/v3'
  347. disc = fixture.DiscoveryList(v2=False, v3=False)
  348. disc.add_v2(v2_compute)
  349. disc.add_v3(v3_compute)
  350. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  351. # This needs to do version discovery to find the version
  352. version = s.get_api_major_version(
  353. service_type='compute', interface='admin')
  354. self.assertEqual((3, 0), version)
  355. self.assertEqual(
  356. self.requests_mock.request_history[-1].url,
  357. self.TEST_COMPUTE_ADMIN)
  358. def test_direct_discovering_with_relative_link(self):
  359. # need to construct list this way for relative
  360. disc = fixture.DiscoveryList(v2=False, v3=False)
  361. disc.add_v2('v2.0')
  362. disc.add_v3('v3')
  363. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  364. a = self.create_auth_plugin()
  365. s = session.Session(auth=a)
  366. catalog_url = s.get_endpoint(
  367. service_type='compute', interface='admin')
  368. disc = discover.get_discovery(s, catalog_url)
  369. url_v2 = disc.url_for(('2', '0'))
  370. url_v3 = disc.url_for(('3', '0'))
  371. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v2.0', url_v2)
  372. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v3', url_v3)
  373. # Verify that passing strings and not tuples works
  374. url_v2 = disc.url_for('2.0')
  375. url_v3 = disc.url_for('3.0')
  376. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v2.0', url_v2)
  377. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v3', url_v3)
  378. def test_discovering_with_relative_anchored_link(self):
  379. # need to construct list this way for relative
  380. disc = fixture.DiscoveryList(v2=False, v3=False)
  381. disc.add_v2('/v2.0')
  382. disc.add_v3('/v3')
  383. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  384. a = self.create_auth_plugin()
  385. s = session.Session(auth=a)
  386. endpoint_v2 = s.get_endpoint(service_type='compute',
  387. interface='admin',
  388. version=(2, 0))
  389. endpoint_v3 = s.get_endpoint(service_type='compute',
  390. interface='admin',
  391. version=(3, 0))
  392. # by the nature of urljoin a relative link with a /path gets joined
  393. # back to the root.
  394. self.assertEqual(self.TEST_COMPUTE_BASE + '/v2.0', endpoint_v2)
  395. self.assertEqual(self.TEST_COMPUTE_BASE + '/v3', endpoint_v3)
  396. def test_discovering_with_protocol_relative(self):
  397. # strip up to and including the : leaving //host/path
  398. path = self.TEST_COMPUTE_ADMIN[self.TEST_COMPUTE_ADMIN.find(':') + 1:]
  399. disc = fixture.DiscoveryList(v2=False, v3=False)
  400. disc.add_v2(path + '/v2.0')
  401. disc.add_v3(path + '/v3')
  402. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  403. a = self.create_auth_plugin()
  404. s = session.Session(auth=a)
  405. endpoint_v2 = s.get_endpoint(service_type='compute',
  406. interface='admin',
  407. version=(2, 0))
  408. endpoint_v3 = s.get_endpoint(service_type='compute',
  409. interface='admin',
  410. version=(3, 0))
  411. # ensures that the http is carried over from the lookup url
  412. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v2.0', endpoint_v2)
  413. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v3', endpoint_v3)
  414. def test_discovering_when_version_missing(self):
  415. # need to construct list this way for relative
  416. disc = fixture.DiscoveryList(v2=False, v3=False)
  417. disc.add_v2('v2.0')
  418. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  419. a = self.create_auth_plugin()
  420. s = session.Session(auth=a)
  421. endpoint_v2 = s.get_endpoint(service_type='compute',
  422. interface='admin',
  423. version=(2, 0))
  424. endpoint_v3 = s.get_endpoint(service_type='compute',
  425. interface='admin',
  426. version=(3, 0))
  427. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v2.0', endpoint_v2)
  428. self.assertIsNone(endpoint_v3)
  429. def test_endpoint_data_no_version(self):
  430. path = self.TEST_COMPUTE_ADMIN[self.TEST_COMPUTE_ADMIN.find(':') + 1:]
  431. disc = fixture.DiscoveryList(v2=False, v3=False)
  432. disc.add_v2(path + '/v2.0')
  433. disc.add_v3(path + '/v3')
  434. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  435. a = self.create_auth_plugin()
  436. s = session.Session(auth=a)
  437. data = a.get_endpoint_data(session=s,
  438. service_type='compute',
  439. interface='admin')
  440. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v3', data.url)
  441. # We should have gotten the version from the URL
  442. self.assertEqual((3, 0), data.api_version)
  443. def test_get_all_version_data_all_interfaces(self):
  444. for interface in ('public', 'internal', 'admin'):
  445. # The version discovery dict will not have a project_id
  446. disc = fixture.DiscoveryList(v2=False, v3=False)
  447. disc.add_nova_microversion(
  448. href=getattr(self.TEST_VOLUME.versions['v3'].discovery,
  449. interface),
  450. id='v3.0', status='CURRENT',
  451. min_version='3.0', version='3.20')
  452. # Adding a v2 version to a service named volumev3 is not
  453. # an error. The service itself is cinder and has more than
  454. # one major version.
  455. disc.add_nova_microversion(
  456. href=getattr(self.TEST_VOLUME.versions['v2'].discovery,
  457. interface),
  458. id='v2.0', status='SUPPORTED')
  459. self.stub_url(
  460. 'GET', [],
  461. base_url=getattr(self.TEST_VOLUME.unversioned,
  462. interface) + '/',
  463. json=disc)
  464. for url in (
  465. self.TEST_COMPUTE_PUBLIC,
  466. self.TEST_COMPUTE_INTERNAL,
  467. self.TEST_COMPUTE_ADMIN):
  468. disc = fixture.DiscoveryList(v2=False, v3=False)
  469. disc.add_microversion(
  470. href=url, id='v2')
  471. disc.add_microversion(
  472. href=url, id='v2.1',
  473. min_version='2.1', max_version='2.35')
  474. self.stub_url('GET', [], base_url=url, json=disc)
  475. a = self.create_auth_plugin()
  476. s = session.Session(auth=a)
  477. identity_endpoint = 'http://127.0.0.1:35357/{}/'.format(self.version)
  478. data = s.get_all_version_data(interface=None)
  479. self.assertEqual({
  480. 'RegionOne': {
  481. 'admin': {
  482. 'block-storage': [{
  483. 'collection': None,
  484. 'max_microversion': None,
  485. 'min_microversion': None,
  486. 'next_min_version': None,
  487. 'not_before': None,
  488. 'raw_status': 'SUPPORTED',
  489. 'status': 'SUPPORTED',
  490. 'url': 'https://block-storage.example.com/admin/v2',
  491. 'version': '2.0'
  492. }, {
  493. 'collection': None,
  494. 'max_microversion': '3.20',
  495. 'min_microversion': '3.0',
  496. 'next_min_version': None,
  497. 'not_before': None,
  498. 'raw_status': 'CURRENT',
  499. 'status': 'CURRENT',
  500. 'url': 'https://block-storage.example.com/admin/v3',
  501. 'version': '3.0'
  502. }],
  503. 'compute': [{
  504. 'collection': None,
  505. 'max_microversion': None,
  506. 'min_microversion': None,
  507. 'next_min_version': None,
  508. 'not_before': None,
  509. 'raw_status': 'stable',
  510. 'status': 'CURRENT',
  511. 'url': 'https://compute.example.com/nova/admin',
  512. 'version': '2.0'
  513. }, {
  514. 'collection': None,
  515. 'max_microversion': '2.35',
  516. 'min_microversion': '2.1',
  517. 'next_min_version': None,
  518. 'not_before': None,
  519. 'raw_status': 'stable',
  520. 'status': 'CURRENT',
  521. 'url': 'https://compute.example.com/nova/admin',
  522. 'version': '2.1'}],
  523. 'identity': [{
  524. 'collection': None,
  525. 'max_microversion': None,
  526. 'min_microversion': None,
  527. 'next_min_version': None,
  528. 'not_before': None,
  529. 'raw_status': None,
  530. 'status': 'CURRENT',
  531. 'url': identity_endpoint,
  532. 'version': self.discovery_version,
  533. }]
  534. },
  535. 'internal': {
  536. 'baremetal': [{
  537. 'collection': None,
  538. 'max_microversion': None,
  539. 'min_microversion': None,
  540. 'next_min_version': None,
  541. 'not_before': None,
  542. 'raw_status': None,
  543. 'status': 'CURRENT',
  544. 'url': 'https://baremetal.example.com/internal/',
  545. 'version': None
  546. }],
  547. 'block-storage': [{
  548. 'collection': None,
  549. 'max_microversion': None,
  550. 'min_microversion': None,
  551. 'next_min_version': None,
  552. 'not_before': None,
  553. 'raw_status': 'SUPPORTED',
  554. 'status': 'SUPPORTED',
  555. 'url': 'https://block-storage.example.com/internal/v2',
  556. 'version': '2.0'
  557. }, {
  558. 'collection': None,
  559. 'max_microversion': '3.20',
  560. 'min_microversion': '3.0',
  561. 'next_min_version': None,
  562. 'not_before': None,
  563. 'raw_status': 'CURRENT',
  564. 'status': 'CURRENT',
  565. 'url': 'https://block-storage.example.com/internal/v3',
  566. 'version': '3.0'
  567. }],
  568. 'compute': [{
  569. 'collection': None,
  570. 'max_microversion': None,
  571. 'min_microversion': None,
  572. 'next_min_version': None,
  573. 'not_before': None,
  574. 'raw_status': 'stable',
  575. 'status': 'CURRENT',
  576. 'url': 'https://compute.example.com/nova/internal',
  577. 'version': '2.0'
  578. }, {
  579. 'collection': None,
  580. 'max_microversion': '2.35',
  581. 'min_microversion': '2.1',
  582. 'next_min_version': None,
  583. 'not_before': None,
  584. 'raw_status': 'stable',
  585. 'status': 'CURRENT',
  586. 'url': 'https://compute.example.com/nova/internal',
  587. 'version': '2.1'
  588. }]
  589. },
  590. 'public': {
  591. 'block-storage': [{
  592. 'collection': None,
  593. 'max_microversion': None,
  594. 'min_microversion': None,
  595. 'next_min_version': None,
  596. 'not_before': None,
  597. 'raw_status': 'SUPPORTED',
  598. 'status': 'SUPPORTED',
  599. 'url': 'https://block-storage.example.com/public/v2',
  600. 'version': '2.0'
  601. }, {
  602. 'collection': None,
  603. 'max_microversion': '3.20',
  604. 'min_microversion': '3.0',
  605. 'next_min_version': None,
  606. 'not_before': None,
  607. 'raw_status': 'CURRENT',
  608. 'status': 'CURRENT',
  609. 'url': 'https://block-storage.example.com/public/v3',
  610. 'version': '3.0'
  611. }],
  612. 'compute': [{
  613. 'collection': None,
  614. 'max_microversion': None,
  615. 'min_microversion': None,
  616. 'next_min_version': None,
  617. 'not_before': None,
  618. 'raw_status': 'stable',
  619. 'status': 'CURRENT',
  620. 'url': 'https://compute.example.com/nova/public',
  621. 'version': '2.0'
  622. }, {
  623. 'collection': None,
  624. 'max_microversion': '2.35',
  625. 'min_microversion': '2.1',
  626. 'next_min_version': None,
  627. 'not_before': None,
  628. 'raw_status': 'stable',
  629. 'status': 'CURRENT',
  630. 'url': 'https://compute.example.com/nova/public',
  631. 'version': '2.1',
  632. }]
  633. }
  634. }
  635. }, data)
  636. def test_get_all_version_data(self):
  637. cinder_disc = fixture.DiscoveryList(v2=False, v3=False)
  638. # The version discovery dict will not have a project_id
  639. cinder_disc.add_nova_microversion(
  640. href=self.TEST_VOLUME.versions['v3'].discovery.public,
  641. id='v3.0', status='CURRENT',
  642. min_version='3.0', version='3.20')
  643. # Adding a v2 version to a service named volumev3 is not
  644. # an error. The service itself is cinder and has more than
  645. # one major version.
  646. cinder_disc.add_nova_microversion(
  647. href=self.TEST_VOLUME.versions['v2'].discovery.public,
  648. id='v2.0', status='SUPPORTED')
  649. self.stub_url(
  650. 'GET', [],
  651. base_url=self.TEST_VOLUME.unversioned.public + '/',
  652. json=cinder_disc)
  653. nova_disc = fixture.DiscoveryList(v2=False, v3=False)
  654. nova_disc.add_microversion(
  655. href=self.TEST_COMPUTE_PUBLIC, id='v2')
  656. nova_disc.add_microversion(
  657. href=self.TEST_COMPUTE_PUBLIC, id='v2.1',
  658. min_version='2.1', max_version='2.35')
  659. self.stub_url(
  660. 'GET', [], base_url=self.TEST_COMPUTE_PUBLIC, json=nova_disc)
  661. a = self.create_auth_plugin()
  662. s = session.Session(auth=a)
  663. data = s.get_all_version_data(interface='public')
  664. self.assertEqual({
  665. 'RegionOne': {
  666. 'public': {
  667. 'block-storage': [{
  668. 'collection': None,
  669. 'max_microversion': None,
  670. 'min_microversion': None,
  671. 'next_min_version': None,
  672. 'not_before': None,
  673. 'raw_status': 'SUPPORTED',
  674. 'status': 'SUPPORTED',
  675. 'url': 'https://block-storage.example.com/public/v2',
  676. 'version': '2.0'
  677. }, {
  678. 'collection': None,
  679. 'max_microversion': '3.20',
  680. 'min_microversion': '3.0',
  681. 'next_min_version': None,
  682. 'not_before': None,
  683. 'raw_status': 'CURRENT',
  684. 'status': 'CURRENT',
  685. 'url': 'https://block-storage.example.com/public/v3',
  686. 'version': '3.0'
  687. }],
  688. 'compute': [{
  689. 'collection': None,
  690. 'max_microversion': None,
  691. 'min_microversion': None,
  692. 'next_min_version': None,
  693. 'not_before': None,
  694. 'raw_status': 'stable',
  695. 'status': 'CURRENT',
  696. 'url': 'https://compute.example.com/nova/public',
  697. 'version': '2.0'
  698. }, {
  699. 'collection': None,
  700. 'max_microversion': '2.35',
  701. 'min_microversion': '2.1',
  702. 'next_min_version': None,
  703. 'not_before': None,
  704. 'raw_status': 'stable',
  705. 'status': 'CURRENT',
  706. 'url': 'https://compute.example.com/nova/public',
  707. 'version': '2.1'
  708. }],
  709. }
  710. }
  711. }, data)
  712. def test_endpoint_data_no_version_no_discovery(self):
  713. a = self.create_auth_plugin()
  714. s = session.Session(auth=a)
  715. data = a.get_endpoint_data(session=s,
  716. service_type='compute',
  717. interface='admin',
  718. discover_versions=False)
  719. self.assertEqual(self.TEST_COMPUTE_ADMIN, data.url)
  720. # There's no version in the URL and no document - we have no idea
  721. self.assertIsNone(data.api_version)
  722. def test_endpoint_data_version_url_no_discovery(self):
  723. a = self.create_auth_plugin()
  724. s = session.Session(auth=a)
  725. data = a.get_endpoint_data(session=s,
  726. service_type='volumev3',
  727. interface='admin',
  728. discover_versions=False)
  729. self.assertEqual(
  730. self.TEST_VOLUME.versions['v3'].service.admin, data.url)
  731. # There's v3 in the URL
  732. self.assertEqual((3, 0), data.api_version)
  733. def test_endpoint_no_version(self):
  734. a = self.create_auth_plugin()
  735. s = session.Session(auth=a)
  736. data = a.get_endpoint(session=s,
  737. service_type='compute',
  738. interface='admin')
  739. self.assertEqual(self.TEST_COMPUTE_ADMIN, data)
  740. def test_endpoint_data_relative_version(self):
  741. # need to construct list this way for relative
  742. disc = fixture.DiscoveryList(v2=False, v3=False)
  743. disc.add_v2('v2.0')
  744. disc.add_v3('v3')
  745. self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
  746. a = self.create_auth_plugin()
  747. s = session.Session(auth=a)
  748. data_v2 = a.get_endpoint_data(session=s,
  749. service_type='compute',
  750. interface='admin',
  751. min_version=(2, 0),
  752. max_version=(2, discover.LATEST))
  753. data_v3 = a.get_endpoint_data(session=s,
  754. service_type='compute',
  755. interface='admin',
  756. min_version=(3, 0),
  757. max_version=(3, discover.LATEST))
  758. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v2.0', data_v2.url)
  759. self.assertEqual(self.TEST_COMPUTE_ADMIN + '/v3', data_v3.url)
  760. def test_get_versioned_data(self):
  761. v2_compute = self.TEST_COMPUTE_ADMIN + '/v2.0'
  762. v3_compute = self.TEST_COMPUTE_ADMIN + '/v3'
  763. disc = fixture.DiscoveryList(v2=False, v3=False)
  764. disc.add_v2(v2_compute)
  765. disc.add_v3(v3_compute)
  766. # Make sure that we don't do more than one discovery call
  767. # register responses such that if the discovery URL is hit more than
  768. # once then the response will be invalid and not point to COMPUTE_ADMIN
  769. resps = [{'json': disc}, {'status_code': 500}]
  770. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  771. a = self.create_auth_plugin()
  772. s = session.Session(auth=a)
  773. data = a.get_endpoint_data(session=s,
  774. service_type='compute',
  775. interface='admin')
  776. self.assertEqual(v3_compute, data.url)
  777. v2_data = data.get_versioned_data(s, min_version='2.0',
  778. max_version='2.latest')
  779. self.assertEqual(v2_compute, v2_data.url)
  780. self.assertEqual(v2_compute, v2_data.service_url)
  781. self.assertEqual(self.TEST_COMPUTE_ADMIN, v2_data.catalog_url)
  782. # Variants that all return v3 data
  783. for vkwargs in (dict(min_version='3.0', max_version='3.latest'),
  784. # min/max spans major versions
  785. dict(min_version='2.0', max_version='3.latest'),
  786. # latest major max
  787. dict(min_version='2.0', max_version='latest'),
  788. # implicit max
  789. dict(min_version='2.0'),
  790. # implicit min/max
  791. dict()):
  792. v3_data = data.get_versioned_data(s, **vkwargs)
  793. self.assertEqual(v3_compute, v3_data.url)
  794. self.assertEqual(v3_compute, v3_data.service_url)
  795. self.assertEqual(self.TEST_COMPUTE_ADMIN, v3_data.catalog_url)
  796. def test_get_current_versioned_data(self):
  797. v2_compute = self.TEST_COMPUTE_ADMIN + '/v2.0'
  798. v3_compute = self.TEST_COMPUTE_ADMIN + '/v3'
  799. disc = fixture.DiscoveryList(v2=False, v3=False)
  800. disc.add_v2(v2_compute)
  801. disc.add_v3(v3_compute)
  802. # Make sure that we don't do more than one discovery call
  803. # register responses such that if the discovery URL is hit more than
  804. # once then the response will be invalid and not point to COMPUTE_ADMIN
  805. resps = [{'json': disc}, {'status_code': 500}]
  806. self.requests_mock.get(self.TEST_COMPUTE_ADMIN, resps)
  807. a = self.create_auth_plugin()
  808. s = session.Session(auth=a)
  809. data = a.get_endpoint_data(session=s,
  810. service_type='compute',
  811. interface='admin')
  812. self.assertEqual(v3_compute, data.url)
  813. v3_data = data.get_current_versioned_data(s)
  814. self.assertEqual(v3_compute, v3_data.url)
  815. self.assertEqual(v3_compute, v3_data.service_url)
  816. self.assertEqual(self.TEST_COMPUTE_ADMIN, v3_data.catalog_url)
  817. self.assertEqual((3, 0), v3_data.api_version)
  818. self.assertIsNone(v3_data.min_microversion)
  819. self.assertIsNone(v3_data.max_microversion)
  820. def test_interface_list(self):
  821. a = self.create_auth_plugin()
  822. s = session.Session(auth=a)
  823. ep = s.get_endpoint(service_type='baremetal',
  824. interface=['internal', 'public'])
  825. self.assertEqual(ep, self.TEST_BAREMETAL_INTERNAL)
  826. ep = s.get_endpoint(service_type='baremetal',
  827. interface=['public', 'internal'])
  828. self.assertEqual(ep, self.TEST_BAREMETAL_INTERNAL)
  829. ep = s.get_endpoint(service_type='compute',
  830. interface=['internal', 'public'])
  831. self.assertEqual(ep, self.TEST_COMPUTE_INTERNAL)
  832. ep = s.get_endpoint(service_type='compute',
  833. interface=['public', 'internal'])
  834. self.assertEqual(ep, self.TEST_COMPUTE_PUBLIC)
  835. def test_get_versioned_data_volume_project_id(self):
  836. disc = fixture.DiscoveryList(v2=False, v3=False)
  837. # The version discovery dict will not have a project_id
  838. disc.add_nova_microversion(
  839. href=self.TEST_VOLUME.versions['v3'].discovery.public,
  840. id='v3.0', status='CURRENT',
  841. min_version='3.0', version='3.20')
  842. # Adding a v2 version to a service named volumev3 is not
  843. # an error. The service itself is cinder and has more than
  844. # one major version.
  845. disc.add_nova_microversion(
  846. href=self.TEST_VOLUME.versions['v2'].discovery.public,
  847. id='v2.0', status='SUPPORTED')
  848. a = self.create_auth_plugin()
  849. s = session.Session(auth=a)
  850. # volume endpoint ends in v3, we should not make an API call
  851. endpoint = a.get_endpoint(session=s,
  852. service_type='volumev3',
  853. interface='public',
  854. version='3.0')
  855. self.assertEqual(self.TEST_VOLUME.catalog.public, endpoint)
  856. resps = [{'json': disc}, {'status_code': 500}]
  857. # We should only try to fetch the versioned discovery url once
  858. self.requests_mock.get(
  859. self.TEST_VOLUME.versions['v3'].discovery.public + '/', resps)
  860. data = a.get_endpoint_data(session=s,
  861. service_type='volumev3',
  862. interface='public')
  863. self.assertEqual(self.TEST_VOLUME.versions['v3'].service.public,
  864. data.url)
  865. v3_data = data.get_versioned_data(
  866. s, min_version='3.0', max_version='3.latest',
  867. project_id=self.project_id)
  868. self.assertEqual(self.TEST_VOLUME.versions['v3'].service.public,
  869. v3_data.url)
  870. self.assertEqual(self.TEST_VOLUME.catalog.public, v3_data.catalog_url)
  871. self.assertEqual((3, 0), v3_data.min_microversion)
  872. self.assertEqual((3, 20), v3_data.max_microversion)
  873. self.assertEqual(self.TEST_VOLUME.versions['v3'].service.public,
  874. v3_data.service_url)
  875. # Because of the v3 optimization before, requesting v2 should now go
  876. # find the unversioned endpoint
  877. self.requests_mock.get(self.TEST_VOLUME.unversioned.public, resps)
  878. v2_data = data.get_versioned_data(
  879. s, min_version='2.0', max_version='2.latest',
  880. project_id=self.project_id)
  881. # Even though we never requested volumev2 from the catalog, we should
  882. # wind up re-constructing it via version discovery and re-appending
  883. # the project_id to the URL
  884. self.assertEqual(self.TEST_VOLUME.versions['v2'].service.public,
  885. v2_data.url)
  886. self.assertEqual(self.TEST_VOLUME.versions['v2'].service.public,
  887. v2_data.service_url)
  888. self.assertEqual(self.TEST_VOLUME.catalog.public, v2_data.catalog_url)
  889. self.assertIsNone(v2_data.min_microversion)
  890. self.assertIsNone(v2_data.max_microversion)
  891. def test_get_versioned_data_volume_project_id_unversioned_first(self):
  892. disc = fixture.DiscoveryList(v2=False, v3=False)
  893. # The version discovery dict will not have a project_id
  894. disc.add_nova_microversion(
  895. href=self.TEST_VOLUME.versions['v3'].discovery.public,
  896. id='v3.0', status='CURRENT',
  897. min_version='3.0', version='3.20')
  898. # Adding a v2 version to a service named volumev3 is not
  899. # an error. The service itself is cinder and has more than
  900. # one major version.
  901. disc.add_nova_microversion(
  902. href=self.TEST_VOLUME.versions['v2'].discovery.public,
  903. id='v2.0', status='SUPPORTED')
  904. a = self.create_auth_plugin()
  905. s = session.Session(auth=a)
  906. # cinder endpoint ends in v3, we should not make an API call
  907. endpoint = a.get_endpoint(session=s,
  908. service_type='volumev3',
  909. interface='public',
  910. version='3.0')
  911. self.assertEqual(self.TEST_VOLUME.catalog.public, endpoint)
  912. resps = [{'json': disc}, {'status_code': 500}]
  913. # We should only try to fetch the unversioned non-project_id url once
  914. # Because the catalog has the versioned endpoint but we constructed
  915. # an unversioned endpoint, the url needs to have a trailing /
  916. self.requests_mock.get(
  917. self.TEST_VOLUME.unversioned.public + '/', resps)
  918. # Fetch v2.0 first - since that doesn't match endpoint optimization,
  919. # it should fetch the unversioned endpoint
  920. v2_data = s.get_endpoint_data(service_type='block-storage',
  921. interface='public',
  922. min_version='2.0',
  923. max_version='2.latest',
  924. project_id=self.project_id)
  925. # Even though we never requested volumev2 from the catalog, we should
  926. # wind up re-constructing it via version discovery and re-appending
  927. # the project_id to the URL
  928. self.assertEqual(self.TEST_VOLUME.versions['v2'].service.public,
  929. v2_data.url)
  930. self.assertEqual(self.TEST_VOLUME.versions['v2'].service.public,
  931. v2_data.service_url)
  932. self.assertEqual(self.TEST_VOLUME.catalog.public, v2_data.catalog_url)
  933. self.assertIsNone(v2_data.min_microversion)
  934. self.assertIsNone(v2_data.max_microversion)
  935. # Since we fetched from the unversioned endpoint to satisfy the
  936. # request for v2, we should have all the relevant data cached in the
  937. # discovery object - and should not fetch anything new.
  938. v3_data = v2_data.get_versioned_data(
  939. s, min_version='3.0', max_version='3.latest',
  940. project_id=self.project_id)
  941. self.assertEqual(self.TEST_VOLUME.versions['v3'].service.public,
  942. v3_data.url)
  943. self.assertEqual(self.TEST_VOLUME.catalog.public, v3_data.catalog_url)
  944. self.assertEqual((3, 0), v3_data.min_microversion)
  945. self.assertEqual((3, 20), v3_data.max_microversion)
  946. self.assertEqual(self.TEST_VOLUME.versions['v3'].service.public,
  947. v3_data.service_url)
  948. def test_trailing_slash_on_computed_endpoint(self):
  949. disc = fixture.DiscoveryList(v2=False, v3=False)
  950. # A versioned URL in the Catalog
  951. disc.add_nova_microversion(
  952. href=self.TEST_VOLUME.versions['v3'].discovery.public,
  953. id='v3.0', status='CURRENT',
  954. min_version='3.0', version='3.20')
  955. a = self.create_auth_plugin()
  956. s = session.Session(auth=a)
  957. # endpoint ends in v3, we will construct the unversioned endpoint.
  958. # Because the catalog has the versioned endpoint but we constructed
  959. # an unversioned endpoint, the url needs to have a trailing /
  960. self.requests_mock.get(
  961. self.TEST_VOLUME.unversioned.public + '/', json=disc)
  962. # We're requesting version 2 of block-storage to make sure we
  963. # trigger the logic constructing the unversioned endpoint from the
  964. # versioned endpoint in the catalog
  965. s.get_endpoint_data(service_type='block-storage',
  966. interface='public',
  967. min_version='2.0',
  968. max_version='2.latest',
  969. project_id=self.project_id)
  970. self.assertTrue(
  971. self.requests_mock.request_history[-1].url.endswith('/'))
  972. def test_no_trailing_slash_on_catalog_endpoint(self):
  973. disc = fixture.DiscoveryList(v2=False, v3=False)
  974. # A versioned URL in the Catalog
  975. disc.add_nova_microversion(
  976. href=self.TEST_COMPUTE_PUBLIC,
  977. id='v2.1', status='CURRENT',
  978. min_version='2.1', version='2.38')
  979. a = self.create_auth_plugin()
  980. s = session.Session(auth=a)
  981. # nova has unversioned endpoint in this catalog. We should not
  982. # modify it.
  983. self.requests_mock.get(self.TEST_COMPUTE_PUBLIC, json=disc)
  984. s.get_endpoint_data(service_type='compute',
  985. interface='public',
  986. min_version='2.1',
  987. max_version='2.latest')
  988. self.assertFalse(
  989. self.requests_mock.request_history[-1].url.endswith('/'))
  990. def test_broken_discovery_endpoint(self):
  991. # Discovery document with a bogus/broken base URL
  992. disc = fixture.DiscoveryList(v2=False, v3=False)
  993. disc.add_nova_microversion(
  994. href='http://internal.example.com',
  995. id='v2.1', status='CURRENT',
  996. min_version='2.1', version='2.38')
  997. a = self.create_auth_plugin()
  998. s = session.Session(auth=a)
  999. self.requests_mock.get(self.TEST_COMPUTE_PUBLIC, json=disc)
  1000. data = s.get_endpoint_data(service_type='compute',
  1001. interface='public',
  1002. min_version='2.1',
  1003. max_version='2.latest')
  1004. # Verify that we get the versioned url based on the catalog url
  1005. self.assertTrue(data.url, self.TEST_COMPUTE_PUBLIC + '/v2.1')
  1006. def test_asking_for_auth_endpoint_ignores_checks(self):
  1007. a = self.create_auth_plugin()
  1008. s = session.Session(auth=a)
  1009. auth_url = s.get_endpoint(service_type='compute',
  1010. interface=plugin.AUTH_INTERFACE)
  1011. self.assertEqual(self.TEST_URL, auth_url)
  1012. def _create_expired_auth_plugin(self, **kwargs):
  1013. expires = _utils.before_utcnow(minutes=20)
  1014. expired_token = self.get_auth_data(expires=expires)
  1015. expired_auth_ref = access.create(body=expired_token)
  1016. a = self.create_auth_plugin(**kwargs)
  1017. a.auth_ref = expired_auth_ref
  1018. return a
  1019. def test_reauthenticate(self):
  1020. a = self._create_expired_auth_plugin()
  1021. expired_auth_ref = a.auth_ref
  1022. s = session.Session(auth=a)
  1023. self.assertIsNot(expired_auth_ref, a.get_access(s))
  1024. def test_no_reauthenticate(self):
  1025. a = self._create_expired_auth_plugin(reauthenticate=False)
  1026. expired_auth_ref = a.auth_ref
  1027. s = session.Session(auth=a)
  1028. self.assertIs(expired_auth_ref, a.get_access(s))
  1029. def test_invalidate(self):
  1030. a = self.create_auth_plugin()
  1031. s = session.Session(auth=a)
  1032. # trigger token fetching
  1033. s.get_auth_headers()
  1034. self.assertTrue(a.auth_ref)
  1035. self.assertTrue(a.invalidate())
  1036. self.assertIsNone(a.auth_ref)
  1037. self.assertFalse(a.invalidate())
  1038. def test_get_auth_properties(self):
  1039. a = self.create_auth_plugin()
  1040. s = session.Session()
  1041. self.assertEqual(self.user_id, a.get_user_id(s))
  1042. self.assertEqual(self.project_id, a.get_project_id(s))
  1043. def assertAccessInfoEqual(self, a, b):
  1044. self.assertEqual(a.auth_token, b.auth_token)
  1045. self.assertEqual(a._data, b._data)
  1046. def test_check_cache_id_match(self):
  1047. a = self.create_auth_plugin()
  1048. b = self.create_auth_plugin()
  1049. self.assertIsNot(a, b)
  1050. self.assertIsNone(a.get_auth_state())
  1051. self.assertIsNone(b.get_auth_state())
  1052. a_id = a.get_cache_id()
  1053. b_id = b.get_cache_id()
  1054. self.assertIsNotNone(a_id)
  1055. self.assertIsNotNone(b_id)
  1056. self.assertEqual(a_id, b_id)
  1057. def test_check_cache_id_no_match(self):
  1058. a = self.create_auth_plugin(project_id='a')
  1059. b = self.create_auth_plugin(project_id='b')
  1060. self.assertIsNot(a, b)
  1061. self.assertIsNone(a.get_auth_state())
  1062. self.assertIsNone(b.get_auth_state())
  1063. a_id = a.get_cache_id()
  1064. b_id = b.get_cache_id()
  1065. self.assertIsNotNone(a_id)
  1066. self.assertIsNotNone(b_id)
  1067. self.assertNotEqual(a_id, b_id)
  1068. def test_get_set_auth_state(self):
  1069. a = self.create_auth_plugin()
  1070. b = self.create_auth_plugin()
  1071. self.assertEqual(a.get_cache_id(), b.get_cache_id())
  1072. s = session.Session()
  1073. a_token = a.get_token(s)
  1074. self.assertEqual(1, self.requests_mock.call_count)
  1075. auth_state = a.get_auth_state()
  1076. self.assertIsNotNone(auth_state)
  1077. b.set_auth_state(auth_state)
  1078. b_token = b.get_token(s)
  1079. self.assertEqual(1, self.requests_mock.call_count)
  1080. self.assertEqual(a_token, b_token)
  1081. self.assertAccessInfoEqual(a.auth_ref, b.auth_ref)
  1082. def test_pathless_url(self):
  1083. disc = fixture.DiscoveryList(v2=False, v3=False)
  1084. url = 'http://path.less.url:1234'
  1085. disc.add_microversion(href=url, id='v2.1')
  1086. self.stub_url('GET', base_url=url, status_code=200, json=disc)
  1087. token = fixture.V2Token()
  1088. service = token.add_service('network')
  1089. service.add_endpoint(public=url, admin=url, internal=url)
  1090. self.stub_url('POST', ['tokens'], base_url=url, json=token)
  1091. v2_auth = identity.V2Password(url, username='u', password='p')
  1092. sess = session.Session(auth=v2_auth)
  1093. data = sess.get_endpoint_data(service_type='network')
  1094. # Discovery ran and returned the URL and its version
  1095. self.assertEqual(url, data.url)
  1096. self.assertEqual((2, 1), data.api_version)
  1097. # Run with a project_id to ensure that path is covered
  1098. self.assertEqual(
  1099. 3, len(list(data._get_discovery_url_choices(project_id='42'))))
  1100. class V3(CommonIdentityTests, utils.TestCase):
  1101. @property
  1102. def version(self):
  1103. return 'v3'
  1104. @property
  1105. def discovery_version(self):
  1106. return '3.0'
  1107. def get_auth_data(self, **kwargs):
  1108. kwargs.setdefault('project_id', self.PROJECT_ID)
  1109. token = fixture.V3Token(**kwargs)
  1110. region = 'RegionOne'
  1111. svc = token.add_service('identity')
  1112. svc.add_standard_endpoints(admin=self.TEST_ADMIN_URL, region=region)
  1113. svc = token.add_service('compute')
  1114. svc.add_standard_endpoints(admin=self.TEST_COMPUTE_ADMIN,
  1115. public=self.TEST_COMPUTE_PUBLIC,
  1116. internal=self.TEST_COMPUTE_INTERNAL,
  1117. region=region)
  1118. svc = token.add_service('volumev2')
  1119. svc.add_standard_endpoints(
  1120. admin=self.TEST_VOLUME.versions['v2'].service.admin,
  1121. public=self.TEST_VOLUME.versions['v2'].service.public,
  1122. internal=self.TEST_VOLUME.versions['v2'].service.internal,
  1123. region=region)
  1124. svc = token.add_service('volumev3')
  1125. svc.add_standard_endpoints(
  1126. admin=self.TEST_VOLUME.versions['v3'].service.admin,
  1127. public=self.TEST_VOLUME.versions['v3'].service.public,
  1128. internal=self.TEST_VOLUME.versions['v3'].service.internal,
  1129. region=region)
  1130. # Add block-storage as a versioned endpoint so that we can test
  1131. # versioned to unversioned inference.
  1132. svc = token.add_service('block-storage')
  1133. svc.add_standard_endpoints(
  1134. admin=self.TEST_VOLUME.versions['v3'].service.admin,
  1135. public=self.TEST_VOLUME.versions['v3'].service.public,
  1136. internal=self.TEST_VOLUME.versions['v3'].service.internal,
  1137. region=region)
  1138. svc = token.add_service('baremetal')
  1139. svc.add_standard_endpoints(
  1140. internal=self.TEST_BAREMETAL_INTERNAL,
  1141. region=region)
  1142. return token
  1143. def stub_auth(self, subject_token=None, **kwargs):
  1144. if not subject_token:
  1145. subject_token = self.TEST_TOKEN
  1146. kwargs.setdefault('headers', {})['X-Subject-Token'] = subject_token
  1147. self.stub_url('POST', ['auth', 'tokens'], **kwargs)
  1148. def create_auth_plugin(self, **kwargs):
  1149. kwargs.setdefault('auth_url', self.TEST_URL)
  1150. kwargs.setdefault('username', self.TEST_USER)
  1151. kwargs.setdefault('password', self.TEST_PASS)
  1152. return identity.V3Password(**kwargs)
  1153. class V2(CommonIdentityTests, utils.TestCase):
  1154. @property
  1155. def version(self):
  1156. return 'v2.0'
  1157. @property
  1158. def discovery_version(self):
  1159. return '2.0'
  1160. def create_auth_plugin(self, **kwargs):
  1161. kwargs.setdefault('auth_url', self.TEST_URL)
  1162. kwargs.setdefault('username', self.TEST_USER)
  1163. kwargs.setdefault('password', self.TEST_PASS)
  1164. try:
  1165. kwargs.setdefault('tenant_id', kwargs.pop('project_id'))
  1166. except KeyError:
  1167. pass
  1168. try:
  1169. kwargs.setdefault('tenant_name', kwargs.pop('project_name'))
  1170. except KeyError:
  1171. pass
  1172. return identity.V2Password(**kwargs)
  1173. def get_auth_data(self, **kwargs):
  1174. kwargs.setdefault('tenant_id', self.PROJECT_ID)
  1175. token = fixture.V2Token(**kwargs)
  1176. region = 'RegionOne'
  1177. svc = token.add_service('identity')
  1178. svc.add_endpoint(admin=self.TEST_ADMIN_URL, region=region,
  1179. public=None, internal=None)
  1180. svc = token.add_service('compute')
  1181. svc.add_endpoint(public=self.TEST_COMPUTE_PUBLIC,
  1182. internal=self.TEST_COMPUTE_INTERNAL,
  1183. admin=self.TEST_COMPUTE_ADMIN,
  1184. region=region)
  1185. svc = token.add_service('volumev2')
  1186. svc.add_endpoint(
  1187. admin=self.TEST_VOLUME.versions['v2'].service.admin,
  1188. public=self.TEST_VOLUME.versions['v2'].service.public,
  1189. internal=self.TEST_VOLUME.versions['v2'].service.internal,
  1190. region=region)
  1191. svc = token.add_service('volumev3')
  1192. svc.add_endpoint(
  1193. admin=self.TEST_VOLUME.versions['v3'].service.admin,
  1194. public=self.TEST_VOLUME.versions['v3'].service.public,
  1195. internal=self.TEST_VOLUME.versions['v3'].service.internal,
  1196. region=region)
  1197. # Add block-storage as a versioned endpoint so that we can test
  1198. # versioned to unversioned inferance.
  1199. svc = token.add_service('block-storage')
  1200. svc.add_endpoint(
  1201. admin=self.TEST_VOLUME.versions['v3'].service.admin,
  1202. public=self.TEST_VOLUME.versions['v3'].service.public,
  1203. internal=self.TEST_VOLUME.versions['v3'].service.internal,
  1204. region=region)
  1205. svc = token.add_service('baremetal')
  1206. svc.add_endpoint(
  1207. public=None, admin=None,
  1208. internal=self.TEST_BAREMETAL_INTERNAL,
  1209. region=region)
  1210. return token
  1211. def stub_auth(self, **kwargs):
  1212. self.stub_url('POST', ['tokens'], **kwargs)
  1213. class CatalogHackTests(utils.TestCase):
  1214. TEST_URL = 'http://keystone.server:5000/v2.0'
  1215. OTHER_URL = 'http://other.server:5000/path'
  1216. IDENTITY = 'identity'
  1217. BASE_URL = 'http://keystone.server:5000/'
  1218. V2_URL = BASE_URL + 'v2.0'
  1219. V3_URL = BASE_URL + 'v3'
  1220. PROJECT_ID = uuid.uuid4().hex
  1221. def test_getting_endpoints(self):
  1222. disc = fixture.DiscoveryList(href=self.BASE_URL)
  1223. self.stub_url('GET',
  1224. ['/'],
  1225. base_url=self.BASE_URL,
  1226. json=disc)
  1227. token = fixture.V2Token()
  1228. service = token.add_service(self.IDENTITY)
  1229. service.add_endpoint(public=self.V2_URL,
  1230. admin=self.V2_URL,
  1231. internal=self.V2_URL)
  1232. self.stub_url('POST',
  1233. ['tokens'],
  1234. base_url=self.V2_URL,
  1235. json=token)
  1236. v2_auth = identity.V2Password(self.V2_URL,
  1237. username=uuid.uuid4().hex,
  1238. password=uuid.uuid4().hex)
  1239. sess = session.Session(auth=v2_auth)
  1240. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1241. interface='public',
  1242. version=(3, 0))
  1243. self.assertEqual(self.V3_URL, endpoint)
  1244. def test_returns_original_when_discover_fails(self):
  1245. token = fixture.V2Token()
  1246. service = token.add_service(self.IDENTITY)
  1247. service.add_endpoint(public=self.V2_URL,
  1248. admin=self.V2_URL,
  1249. internal=self.V2_URL)
  1250. self.stub_url('POST',
  1251. ['tokens'],
  1252. base_url=self.V2_URL,
  1253. json=token)
  1254. self.stub_url('GET', [], base_url=self.BASE_URL, status_code=404)
  1255. self.stub_url('GET', [], base_url=self.V2_URL, status_code=404)
  1256. v2_auth = identity.V2Password(self.V2_URL,
  1257. username=uuid.uuid4().hex,
  1258. password=uuid.uuid4().hex)
  1259. sess = session.Session(auth=v2_auth)
  1260. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1261. interface='public',
  1262. version=(3, 0))
  1263. self.assertEqual(self.V2_URL, endpoint)
  1264. def test_getting_endpoints_project_id_and_trailing_slash_in_disc_url(self):
  1265. # Test that when requesting a v3 endpoint and having a project in the
  1266. # session but only the v2 endpoint with a trailing slash in the
  1267. # catalog, we can still discover the v3 endpoint.
  1268. disc = fixture.DiscoveryList(href=self.BASE_URL)
  1269. self.stub_url('GET',
  1270. ['/'],
  1271. base_url=self.BASE_URL,
  1272. json=disc)
  1273. # Create a project-scoped token. This will exercise the flow in the
  1274. # discovery URL sequence where a project ID exists in the token but
  1275. # there is no project ID in the URL.
  1276. token = fixture.V3Token(project_id=self.PROJECT_ID)
  1277. # Add only a v2 endpoint with a trailing slash
  1278. service = token.add_service(self.IDENTITY)
  1279. service.add_endpoint('public', self.V2_URL + '/')
  1280. service.add_endpoint('admin', self.V2_URL + '/')
  1281. # Auth with v3
  1282. kwargs = {'headers': {'X-Subject-Token': self.TEST_TOKEN}}
  1283. self.stub_url('POST',
  1284. ['auth', 'tokens'],
  1285. base_url=self.V3_URL,
  1286. json=token, **kwargs)
  1287. v3_auth = identity.V3Password(self.V3_URL,
  1288. username=uuid.uuid4().hex,
  1289. password=uuid.uuid4().hex)
  1290. sess = session.Session(auth=v3_auth)
  1291. # Try to get a v3 endpoint
  1292. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1293. interface='public',
  1294. version=(3, 0))
  1295. self.assertEqual(self.V3_URL, endpoint)
  1296. def test_returns_original_skipping_discovery(self):
  1297. token = fixture.V2Token()
  1298. service = token.add_service(self.IDENTITY)
  1299. service.add_endpoint(public=self.V2_URL,
  1300. admin=self.V2_URL,
  1301. internal=self.V2_URL)
  1302. self.stub_url('POST',
  1303. ['tokens'],
  1304. base_url=self.V2_URL,
  1305. json=token)
  1306. v2_auth = identity.V2Password(self.V2_URL,
  1307. username=uuid.uuid4().hex,
  1308. password=uuid.uuid4().hex)
  1309. sess = session.Session(auth=v2_auth)
  1310. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1311. interface='public',
  1312. skip_discovery=True,
  1313. version=(3, 0))
  1314. self.assertEqual(self.V2_URL, endpoint)
  1315. def test_endpoint_override_skips_discovery(self):
  1316. token = fixture.V2Token()
  1317. service = token.add_service(self.IDENTITY)
  1318. service.add_endpoint(public=self.V2_URL,
  1319. admin=self.V2_URL,
  1320. internal=self.V2_URL)
  1321. self.stub_url('POST',
  1322. ['tokens'],
  1323. base_url=self.V2_URL,
  1324. json=token)
  1325. v2_auth = identity.V2Password(self.V2_URL,
  1326. username=uuid.uuid4().hex,
  1327. password=uuid.uuid4().hex)
  1328. sess = session.Session(auth=v2_auth)
  1329. endpoint = sess.get_endpoint(endpoint_override=self.OTHER_URL,
  1330. service_type=self.IDENTITY,
  1331. interface='public',
  1332. version=(3, 0))
  1333. self.assertEqual(self.OTHER_URL, endpoint)
  1334. def test_endpoint_override_data_runs_discovery(self):
  1335. common_disc = fixture.DiscoveryList(v2=False, v3=False)
  1336. common_disc.add_microversion(href=self.OTHER_URL, id='v2.1',
  1337. min_version='2.1', max_version='2.35')
  1338. common_m = self.stub_url('GET',
  1339. base_url=self.OTHER_URL,
  1340. status_code=200,
  1341. json=common_disc)
  1342. token = fixture.V2Token()
  1343. service = token.add_service(self.IDENTITY)
  1344. service.add_endpoint(public=self.V2_URL,
  1345. admin=self.V2_URL,
  1346. internal=self.V2_URL)
  1347. self.stub_url('POST',
  1348. ['tokens'],
  1349. base_url=self.V2_URL,
  1350. json=token)
  1351. v2_auth = identity.V2Password(self.V2_URL,
  1352. username=uuid.uuid4().hex,
  1353. password=uuid.uuid4().hex)
  1354. sess = session.Session(auth=v2_auth)
  1355. data = sess.get_endpoint_data(endpoint_override=self.OTHER_URL,
  1356. service_type=self.IDENTITY,
  1357. interface='public',
  1358. min_version=(2, 0),
  1359. max_version=(2, discover.LATEST))
  1360. self.assertTrue(common_m.called)
  1361. self.assertEqual(self.OTHER_URL, data.url)
  1362. self.assertEqual((2, 1), data.min_microversion)
  1363. self.assertEqual((2, 35), data.max_microversion)
  1364. self.assertEqual((2, 1), data.api_version)
  1365. def test_forcing_discovery(self):
  1366. v2_disc = fixture.V2Discovery(self.V2_URL)
  1367. common_disc = fixture.DiscoveryList(href=self.BASE_URL)
  1368. v2_m = self.stub_url('GET',
  1369. ['v2.0'],
  1370. base_url=self.BASE_URL,
  1371. status_code=200,
  1372. json={'version': v2_disc})
  1373. common_m = self.stub_url('GET',
  1374. [],
  1375. base_url=self.BASE_URL,
  1376. status_code=300,
  1377. json=common_disc)
  1378. token = fixture.V2Token()
  1379. service = token.add_service(self.IDENTITY)
  1380. service.add_endpoint(public=self.V2_URL,
  1381. admin=self.V2_URL,
  1382. internal=self.V2_URL)
  1383. self.stub_url('POST',
  1384. ['tokens'],
  1385. base_url=self.V2_URL,
  1386. json=token)
  1387. v2_auth = identity.V2Password(self.V2_URL,
  1388. username=uuid.uuid4().hex,
  1389. password=uuid.uuid4().hex)
  1390. sess = session.Session(auth=v2_auth)
  1391. # v2 auth with v2 url doesn't make any discovery calls.
  1392. self.assertFalse(v2_m.called)
  1393. self.assertFalse(common_m.called)
  1394. data = sess.get_endpoint_data(service_type=self.IDENTITY,
  1395. discover_versions=True)
  1396. # We should get the v2 document, but not the unversioned
  1397. self.assertTrue(v2_m.called)
  1398. self.assertFalse(common_m.called)
  1399. # got v2 url
  1400. self.assertEqual(self.V2_URL, data.url)
  1401. self.assertEqual((2, 0), data.api_version)
  1402. def test_forcing_discovery_list_returns_url(self):
  1403. common_disc = fixture.DiscoveryList(href=self.BASE_URL)
  1404. # 2.0 doesn't usually return a list. This is testing that if
  1405. # the catalog url returns an endpoint that has a discovery document
  1406. # with more than one URL and that a different url would be returned
  1407. # by "return the latest" rules, that we get the info of the url from
  1408. # the catalog if we don't provide a version but do provide
  1409. # discover_versions
  1410. v2_m = self.stub_url('GET',
  1411. ['v2.0'],
  1412. base_url=self.BASE_URL,
  1413. status_code=200,
  1414. json=common_disc)
  1415. token = fixture.V2Token()
  1416. service = token.add_service(self.IDENTITY)
  1417. service.add_endpoint(public=self.V2_URL,
  1418. admin=self.V2_URL,
  1419. internal=self.V2_URL)
  1420. self.stub_url('POST',
  1421. ['tokens'],
  1422. base_url=self.V2_URL,
  1423. json=token)
  1424. v2_auth = identity.V2Password(self.V2_URL,
  1425. username=uuid.uuid4().hex,
  1426. password=uuid.uuid4().hex)
  1427. sess = session.Session(auth=v2_auth)
  1428. # v2 auth with v2 url doesn't make any discovery calls.
  1429. self.assertFalse(v2_m.called)
  1430. data = sess.get_endpoint_data(service_type=self.IDENTITY,
  1431. discover_versions=True)
  1432. # We should make the one call
  1433. self.assertTrue(v2_m.called)
  1434. # got v2 url
  1435. self.assertEqual(self.V2_URL, data.url)
  1436. self.assertEqual((2, 0), data.api_version)
  1437. def test_latest_version_gets_latest_version(self):
  1438. common_disc = fixture.DiscoveryList(href=self.BASE_URL)
  1439. # 2.0 doesn't usually return a list. But we're testing version matching
  1440. # rules, so it's nice to ensure that we don't fallback to something
  1441. v2_m = self.stub_url('GET',
  1442. base_url=self.BASE_URL,
  1443. status_code=200,
  1444. json=common_disc)
  1445. token = fixture.V2Token()
  1446. service = token.add_service(self.IDENTITY)
  1447. service.add_endpoint(public=self.V2_URL,
  1448. admin=self.V2_URL,
  1449. internal=self.V2_URL)
  1450. self.stub_url('POST',
  1451. ['tokens'],
  1452. base_url=self.V2_URL,
  1453. json=token)
  1454. v2_auth = identity.V2Password(self.V2_URL,
  1455. username=uuid.uuid4().hex,
  1456. password=uuid.uuid4().hex)
  1457. sess = session.Session(auth=v2_auth)
  1458. # v2 auth with v2 url doesn't make any discovery calls.
  1459. self.assertFalse(v2_m.called)
  1460. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1461. version='latest')
  1462. # We should make the one call
  1463. self.assertTrue(v2_m.called)
  1464. # And get the v3 url
  1465. self.assertEqual(self.V3_URL, endpoint)
  1466. # Make sure latest logic works for min and max version
  1467. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1468. max_version='latest')
  1469. self.assertEqual(self.V3_URL, endpoint)
  1470. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1471. min_version='latest')
  1472. self.assertEqual(self.V3_URL, endpoint)
  1473. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1474. min_version='latest',
  1475. max_version='latest')
  1476. self.assertEqual(self.V3_URL, endpoint)
  1477. self.assertRaises(ValueError, sess.get_endpoint,
  1478. service_type=self.IDENTITY,
  1479. min_version='latest', max_version='3.0')
  1480. def test_version_range(self):
  1481. v2_disc = fixture.V2Discovery(self.V2_URL)
  1482. common_disc = fixture.DiscoveryList(href=self.BASE_URL)
  1483. def stub_urls():
  1484. v2_m = self.stub_url('GET',
  1485. ['v2.0'],
  1486. base_url=self.BASE_URL,
  1487. status_code=200,
  1488. json={'version': v2_disc})
  1489. common_m = self.stub_url('GET',
  1490. base_url=self.BASE_URL,
  1491. status_code=200,
  1492. json=common_disc)
  1493. return v2_m, common_m
  1494. v2_m, common_m = stub_urls()
  1495. token = fixture.V2Token()
  1496. service = token.add_service(self.IDENTITY)
  1497. service.add_endpoint(public=self.V2_URL,
  1498. admin=self.V2_URL,
  1499. internal=self.V2_URL)
  1500. self.stub_url('POST',
  1501. ['tokens'],
  1502. base_url=self.V2_URL,
  1503. json=token)
  1504. v2_auth = identity.V2Password(self.V2_URL,
  1505. username=uuid.uuid4().hex,
  1506. password=uuid.uuid4().hex)
  1507. sess = session.Session(auth=v2_auth)
  1508. # v2 auth with v2 url doesn't make any discovery calls.
  1509. self.assertFalse(v2_m.called)
  1510. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1511. min_version='2.0', max_version='3.0')
  1512. # We should make the one call
  1513. self.assertFalse(v2_m.called)
  1514. self.assertTrue(common_m.called)
  1515. # And get the v3 url
  1516. self.assertEqual(self.V3_URL, endpoint)
  1517. v2_m, common_m = stub_urls()
  1518. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1519. min_version='1', max_version='2')
  1520. # We should make no calls - we peek in the cache
  1521. self.assertFalse(v2_m.called)
  1522. self.assertFalse(common_m.called)
  1523. # And get the v2 url
  1524. self.assertEqual(self.V2_URL, endpoint)
  1525. v2_m, common_m = stub_urls()
  1526. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1527. min_version='4')
  1528. # We should make no more calls
  1529. self.assertFalse(v2_m.called)
  1530. self.assertFalse(common_m.called)
  1531. # And get no url
  1532. self.assertIsNone(endpoint)
  1533. v2_m, common_m = stub_urls()
  1534. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1535. min_version='2')
  1536. # We should make no more calls
  1537. self.assertFalse(v2_m.called)
  1538. self.assertFalse(common_m.called)
  1539. # And get the v3 url
  1540. self.assertEqual(self.V3_URL, endpoint)
  1541. v2_m, common_m = stub_urls()
  1542. self.assertRaises(ValueError, sess.get_endpoint,
  1543. service_type=self.IDENTITY, version=3,
  1544. min_version='2')
  1545. # We should make no more calls
  1546. self.assertFalse(v2_m.called)
  1547. self.assertFalse(common_m.called)
  1548. def test_get_endpoint_data(self):
  1549. common_disc = fixture.DiscoveryList(v2=False, v3=False)
  1550. common_disc.add_microversion(href=self.OTHER_URL, id='v2.1',
  1551. min_version='2.1', max_version='2.35')
  1552. common_m = self.stub_url('GET',
  1553. base_url=self.OTHER_URL,
  1554. status_code=200,
  1555. json=common_disc)
  1556. token = fixture.V2Token()
  1557. service = token.add_service('network')
  1558. service.add_endpoint(public=self.OTHER_URL,
  1559. admin=self.OTHER_URL,
  1560. internal=self.OTHER_URL)
  1561. self.stub_url('POST',
  1562. ['tokens'],
  1563. base_url=self.V2_URL,
  1564. json=token)
  1565. v2_auth = identity.V2Password(self.V2_URL,
  1566. username=uuid.uuid4().hex,
  1567. password=uuid.uuid4().hex)
  1568. sess = session.Session(auth=v2_auth)
  1569. # v2 auth with v2 url doesn't make any discovery calls.
  1570. self.assertFalse(common_m.called)
  1571. data = sess.get_endpoint_data(service_type='network',
  1572. min_version='2.0', max_version='3.0')
  1573. # We should make the one call
  1574. self.assertTrue(common_m.called)
  1575. # And get the v3 url
  1576. self.assertEqual(self.OTHER_URL, data.url)
  1577. self.assertEqual((2, 1), data.min_microversion)
  1578. self.assertEqual((2, 35), data.max_microversion)
  1579. self.assertEqual((2, 1), data.api_version)
  1580. def test_get_endpoint_data_compute(self):
  1581. common_disc = fixture.DiscoveryList(v2=False, v3=False)
  1582. common_disc.add_nova_microversion(href=self.OTHER_URL, id='v2.1',
  1583. min_version='2.1', version='2.35')
  1584. common_m = self.stub_url('GET',
  1585. base_url=self.OTHER_URL,
  1586. status_code=200,
  1587. json=common_disc)
  1588. token = fixture.V2Token()
  1589. service = token.add_service('compute')
  1590. service.add_endpoint(public=self.OTHER_URL,
  1591. admin=self.OTHER_URL,
  1592. internal=self.OTHER_URL)
  1593. self.stub_url('POST',
  1594. ['tokens'],
  1595. base_url=self.V2_URL,
  1596. json=token)
  1597. v2_auth = identity.V2Password(self.V2_URL,
  1598. username=uuid.uuid4().hex,
  1599. password=uuid.uuid4().hex)
  1600. sess = session.Session(auth=v2_auth)
  1601. # v2 auth with v2 url doesn't make any discovery calls.
  1602. self.assertFalse(common_m.called)
  1603. data = sess.get_endpoint_data(service_type='compute',
  1604. min_version='2.0', max_version='3.0')
  1605. # We should make the one call
  1606. self.assertTrue(common_m.called)
  1607. # And get the v3 url
  1608. self.assertEqual(self.OTHER_URL, data.url)
  1609. self.assertEqual((2, 1), data.min_microversion)
  1610. self.assertEqual((2, 35), data.max_microversion)
  1611. self.assertEqual((2, 1), data.api_version)
  1612. def test_getting_endpoints_on_auth_interface(self):
  1613. disc = fixture.DiscoveryList(href=self.BASE_URL)
  1614. self.stub_url('GET',
  1615. ['/'],
  1616. base_url=self.BASE_URL,
  1617. status_code=300,
  1618. json=disc)
  1619. token = fixture.V2Token()
  1620. service = token.add_service(self.IDENTITY)
  1621. service.add_endpoint(public=self.V2_URL,
  1622. admin=self.V2_URL,
  1623. internal=self.V2_URL)
  1624. self.stub_url('POST',
  1625. ['tokens'],
  1626. base_url=self.V2_URL,
  1627. json=token)
  1628. v2_auth = identity.V2Password(self.V2_URL,
  1629. username=uuid.uuid4().hex,
  1630. password=uuid.uuid4().hex)
  1631. sess = session.Session(auth=v2_auth)
  1632. endpoint = sess.get_endpoint(interface=plugin.AUTH_INTERFACE,
  1633. version=(3, 0))
  1634. self.assertEqual(self.V3_URL, endpoint)
  1635. def test_setting_no_discover_hack(self):
  1636. v2_disc = fixture.V2Discovery(self.V2_URL)
  1637. common_disc = fixture.DiscoveryList(href=self.BASE_URL)
  1638. v2_m = self.stub_url('GET',
  1639. ['v2.0'],
  1640. base_url=self.BASE_URL,
  1641. status_code=200,
  1642. json=v2_disc)
  1643. common_m = self.stub_url('GET',
  1644. [],
  1645. base_url=self.BASE_URL,
  1646. status_code=300,
  1647. json=common_disc)
  1648. resp_text = uuid.uuid4().hex
  1649. resp_m = self.stub_url('GET',
  1650. ['v3', 'path'],
  1651. base_url=self.BASE_URL,
  1652. status_code=200,
  1653. text=resp_text)
  1654. # it doesn't matter that we auth with v2 here, discovery hack is in
  1655. # base. All identity endpoints point to v2 urls.
  1656. token = fixture.V2Token()
  1657. service = token.add_service(self.IDENTITY)
  1658. service.add_endpoint(public=self.V2_URL,
  1659. admin=self.V2_URL,
  1660. internal=self.V2_URL)
  1661. self.stub_url('POST',
  1662. ['tokens'],
  1663. base_url=self.V2_URL,
  1664. json=token)
  1665. v2_auth = identity.V2Password(self.V2_URL,
  1666. username=uuid.uuid4().hex,
  1667. password=uuid.uuid4().hex)
  1668. sess = session.Session(auth=v2_auth)
  1669. # v2 auth with v2 url doesn't make any discovery calls.
  1670. self.assertFalse(v2_m.called)
  1671. self.assertFalse(common_m.called)
  1672. # v3 endpoint with hack will strip v2 suffix and call root discovery
  1673. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1674. version=(3, 0),
  1675. allow_version_hack=True)
  1676. # got v3 url
  1677. self.assertEqual(self.V3_URL, endpoint)
  1678. # only called root discovery.
  1679. self.assertFalse(v2_m.called)
  1680. self.assertTrue(common_m.called_once)
  1681. # with hack turned off it calls v2 discovery and finds nothing
  1682. endpoint = sess.get_endpoint(service_type=self.IDENTITY,
  1683. version=(3, 0),
  1684. allow_version_hack=False)
  1685. self.assertIsNone(endpoint)
  1686. # this one called v2
  1687. self.assertTrue(v2_m.called_once)
  1688. self.assertTrue(common_m.called_once)
  1689. # get_endpoint returning None raises EndpointNotFound when requesting
  1690. self.assertRaises(exceptions.EndpointNotFound,
  1691. sess.get,
  1692. '/path',
  1693. endpoint_filter={'service_type': 'identity',
  1694. 'version': (3, 0),
  1695. 'allow_version_hack': False})
  1696. self.assertFalse(resp_m.called)
  1697. # works when allow_version_hack is set
  1698. resp = sess.get('/path',
  1699. endpoint_filter={'service_type': 'identity',
  1700. 'version': (3, 0),
  1701. 'allow_version_hack': True})
  1702. self.assertTrue(resp_m.called_once)
  1703. self.assertEqual(resp_text, resp.text)
  1704. class GenericPlugin(plugin.BaseAuthPlugin):
  1705. BAD_TOKEN = uuid.uuid4().hex
  1706. def __init__(self):
  1707. super(GenericPlugin, self).__init__()
  1708. self.endpoint = 'http://keystone.host:5000'
  1709. self.headers = {'headerA': 'valueA',
  1710. 'headerB': 'valueB'}
  1711. self.cert = '/path/to/cert'
  1712. self.connection_params = {'cert': self.cert, 'verify': False}
  1713. def url(self, prefix):
  1714. return '%s/%s' % (self.endpoint, prefix)
  1715. def get_token(self, session, **kwargs):
  1716. # NOTE(jamielennox): by specifying get_headers this should not be used
  1717. return self.BAD_TOKEN
  1718. def get_headers(self, session, **kwargs):
  1719. return self.headers
  1720. def get_endpoint(self, session, **kwargs):
  1721. return self.endpoint
  1722. def get_connection_params(self, session, **kwargs):
  1723. return self.connection_params
  1724. class GenericAuthPluginTests(utils.TestCase):
  1725. # filter doesn't matter to GenericPlugin, but we have to specify one
  1726. ENDPOINT_FILTER = {uuid.uuid4().hex: uuid.uuid4().hex}
  1727. def setUp(self):
  1728. super(GenericAuthPluginTests, self).setUp()
  1729. self.auth = GenericPlugin()
  1730. self.session = session.Session(auth=self.auth)
  1731. def test_setting_headers(self):
  1732. text = uuid.uuid4().hex
  1733. self.stub_url('GET', base_url=self.auth.url('prefix'), text=text)
  1734. resp = self.session.get('prefix', endpoint_filter=self.ENDPOINT_FILTER)
  1735. self.assertEqual(text, resp.text)
  1736. for k, v in self.auth.headers.items():
  1737. self.assertRequestHeaderEqual(k, v)
  1738. self.assertIsNone(self.session.get_token())
  1739. self.assertEqual(self.auth.headers,
  1740. self.session.get_auth_headers())
  1741. self.assertNotIn('X-Auth-Token',
  1742. self.requests_mock.last_request.headers)
  1743. def test_setting_connection_params(self):
  1744. text = uuid.uuid4().hex
  1745. self.stub_url('GET', base_url=self.auth.url('prefix'), text=text)
  1746. resp = self.session.get('prefix',
  1747. endpoint_filter=self.ENDPOINT_FILTER)
  1748. self.assertEqual(text, resp.text)
  1749. # the cert and verify values passed to request are those that were
  1750. # returned from the auth plugin as connection params.
  1751. self.assertEqual(self.auth.cert, self.requests_mock.last_request.cert)
  1752. self.assertFalse(self.requests_mock.last_request.verify)
  1753. def test_setting_bad_connection_params(self):
  1754. # The uuid name parameter here is unknown and not in the allowed params
  1755. # to be returned to the session and so an error will be raised.
  1756. name = uuid.uuid4().hex
  1757. self.auth.connection_params[name] = uuid.uuid4().hex
  1758. e = self.assertRaises(exceptions.UnsupportedParameters,
  1759. self.session.get,
  1760. 'prefix',
  1761. endpoint_filter=self.ENDPOINT_FILTER)
  1762. self.assertIn(name, str(e))
  1763. class DiscoveryFailures(utils.TestCase):
  1764. TEST_ROOT_URL = 'http://127.0.0.1:5000/'
  1765. def test_connection_error(self):
  1766. self.requests_mock.get(self.TEST_ROOT_URL,
  1767. exc=exceptions.ConnectionError)
  1768. sess = session.Session()
  1769. p = identity.generic.password.Password(self.TEST_ROOT_URL)
  1770. self.assertRaises(exceptions.DiscoveryFailure, p.get_auth_ref, sess)
  1771. def test_client_exception(self):
  1772. self.requests_mock.get(self.TEST_ROOT_URL,
  1773. exc=exceptions.ClientException)
  1774. sess = session.Session()
  1775. p = identity.generic.password.Password(self.TEST_ROOT_URL)
  1776. self.assertRaises(exceptions.ClientException, p.get_auth_ref, sess)
  1777. def test_ssl_error(self):
  1778. self.requests_mock.get(self.TEST_ROOT_URL, exc=exceptions.SSLError)
  1779. sess = session.Session()
  1780. p = identity.generic.password.Password(self.TEST_ROOT_URL)
  1781. self.assertRaises(exceptions.DiscoveryFailure, p.get_auth_ref, sess)