test__metadata.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. # Copyright 2016 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import datetime
  15. import json
  16. import os
  17. import mock
  18. import pytest
  19. from six.moves import http_client
  20. from six.moves import reload_module
  21. from google.auth import _helpers
  22. from google.auth import environment_vars
  23. from google.auth import exceptions
  24. from google.auth import transport
  25. from google.auth.compute_engine import _metadata
  26. PATH = "instance/service-accounts/default"
  27. def make_request(data, status=http_client.OK, headers=None, retry=False):
  28. response = mock.create_autospec(transport.Response, instance=True)
  29. response.status = status
  30. response.data = _helpers.to_bytes(data)
  31. response.headers = headers or {}
  32. request = mock.create_autospec(transport.Request)
  33. if retry:
  34. request.side_effect = [exceptions.TransportError(), response]
  35. else:
  36. request.return_value = response
  37. return request
  38. def test_ping_success():
  39. request = make_request("", headers=_metadata._METADATA_HEADERS)
  40. assert _metadata.ping(request)
  41. request.assert_called_once_with(
  42. method="GET",
  43. url=_metadata._METADATA_IP_ROOT,
  44. headers=_metadata._METADATA_HEADERS,
  45. timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
  46. )
  47. def test_ping_success_retry():
  48. request = make_request("", headers=_metadata._METADATA_HEADERS, retry=True)
  49. assert _metadata.ping(request)
  50. request.assert_called_with(
  51. method="GET",
  52. url=_metadata._METADATA_IP_ROOT,
  53. headers=_metadata._METADATA_HEADERS,
  54. timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
  55. )
  56. assert request.call_count == 2
  57. def test_ping_failure_bad_flavor():
  58. request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"})
  59. assert not _metadata.ping(request)
  60. def test_ping_failure_connection_failed():
  61. request = make_request("")
  62. request.side_effect = exceptions.TransportError()
  63. assert not _metadata.ping(request)
  64. def _test_ping_success_custom_root():
  65. request = make_request("", headers=_metadata._METADATA_HEADERS)
  66. fake_ip = "1.2.3.4"
  67. os.environ[environment_vars.GCE_METADATA_IP] = fake_ip
  68. reload_module(_metadata)
  69. try:
  70. assert _metadata.ping(request)
  71. finally:
  72. del os.environ[environment_vars.GCE_METADATA_IP]
  73. reload_module(_metadata)
  74. request.assert_called_once_with(
  75. method="GET",
  76. url="http://" + fake_ip,
  77. headers=_metadata._METADATA_HEADERS,
  78. timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
  79. )
  80. def test_get_success_json():
  81. key, value = "foo", "bar"
  82. data = json.dumps({key: value})
  83. request = make_request(data, headers={"content-type": "application/json"})
  84. result = _metadata.get(request, PATH)
  85. request.assert_called_once_with(
  86. method="GET",
  87. url=_metadata._METADATA_ROOT + PATH,
  88. headers=_metadata._METADATA_HEADERS,
  89. )
  90. assert result[key] == value
  91. def test_get_success_retry():
  92. key, value = "foo", "bar"
  93. data = json.dumps({key: value})
  94. request = make_request(
  95. data, headers={"content-type": "application/json"}, retry=True
  96. )
  97. result = _metadata.get(request, PATH)
  98. request.assert_called_with(
  99. method="GET",
  100. url=_metadata._METADATA_ROOT + PATH,
  101. headers=_metadata._METADATA_HEADERS,
  102. )
  103. assert request.call_count == 2
  104. assert result[key] == value
  105. def test_get_success_text():
  106. data = "foobar"
  107. request = make_request(data, headers={"content-type": "text/plain"})
  108. result = _metadata.get(request, PATH)
  109. request.assert_called_once_with(
  110. method="GET",
  111. url=_metadata._METADATA_ROOT + PATH,
  112. headers=_metadata._METADATA_HEADERS,
  113. )
  114. assert result == data
  115. def test_get_success_params():
  116. data = "foobar"
  117. request = make_request(data, headers={"content-type": "text/plain"})
  118. params = {"recursive": "true"}
  119. result = _metadata.get(request, PATH, params=params)
  120. request.assert_called_once_with(
  121. method="GET",
  122. url=_metadata._METADATA_ROOT + PATH + "?recursive=true",
  123. headers=_metadata._METADATA_HEADERS,
  124. )
  125. assert result == data
  126. def test_get_success_recursive_and_params():
  127. data = "foobar"
  128. request = make_request(data, headers={"content-type": "text/plain"})
  129. params = {"recursive": "false"}
  130. result = _metadata.get(request, PATH, recursive=True, params=params)
  131. request.assert_called_once_with(
  132. method="GET",
  133. url=_metadata._METADATA_ROOT + PATH + "?recursive=true",
  134. headers=_metadata._METADATA_HEADERS,
  135. )
  136. assert result == data
  137. def test_get_success_recursive():
  138. data = "foobar"
  139. request = make_request(data, headers={"content-type": "text/plain"})
  140. result = _metadata.get(request, PATH, recursive=True)
  141. request.assert_called_once_with(
  142. method="GET",
  143. url=_metadata._METADATA_ROOT + PATH + "?recursive=true",
  144. headers=_metadata._METADATA_HEADERS,
  145. )
  146. assert result == data
  147. def _test_get_success_custom_root_new_variable():
  148. request = make_request("{}", headers={"content-type": "application/json"})
  149. fake_root = "another.metadata.service"
  150. os.environ[environment_vars.GCE_METADATA_HOST] = fake_root
  151. reload_module(_metadata)
  152. try:
  153. _metadata.get(request, PATH)
  154. finally:
  155. del os.environ[environment_vars.GCE_METADATA_HOST]
  156. reload_module(_metadata)
  157. request.assert_called_once_with(
  158. method="GET",
  159. url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH),
  160. headers=_metadata._METADATA_HEADERS,
  161. )
  162. def _test_get_success_custom_root_old_variable():
  163. request = make_request("{}", headers={"content-type": "application/json"})
  164. fake_root = "another.metadata.service"
  165. os.environ[environment_vars.GCE_METADATA_ROOT] = fake_root
  166. reload_module(_metadata)
  167. try:
  168. _metadata.get(request, PATH)
  169. finally:
  170. del os.environ[environment_vars.GCE_METADATA_ROOT]
  171. reload_module(_metadata)
  172. request.assert_called_once_with(
  173. method="GET",
  174. url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH),
  175. headers=_metadata._METADATA_HEADERS,
  176. )
  177. def test_get_failure():
  178. request = make_request("Metadata error", status=http_client.NOT_FOUND)
  179. with pytest.raises(exceptions.TransportError) as excinfo:
  180. _metadata.get(request, PATH)
  181. assert excinfo.match(r"Metadata error")
  182. request.assert_called_once_with(
  183. method="GET",
  184. url=_metadata._METADATA_ROOT + PATH,
  185. headers=_metadata._METADATA_HEADERS,
  186. )
  187. def test_get_failure_connection_failed():
  188. request = make_request("")
  189. request.side_effect = exceptions.TransportError()
  190. with pytest.raises(exceptions.TransportError) as excinfo:
  191. _metadata.get(request, PATH)
  192. assert excinfo.match(r"Compute Engine Metadata server unavailable")
  193. request.assert_called_with(
  194. method="GET",
  195. url=_metadata._METADATA_ROOT + PATH,
  196. headers=_metadata._METADATA_HEADERS,
  197. )
  198. assert request.call_count == 5
  199. def test_get_failure_bad_json():
  200. request = make_request("{", headers={"content-type": "application/json"})
  201. with pytest.raises(exceptions.TransportError) as excinfo:
  202. _metadata.get(request, PATH)
  203. assert excinfo.match(r"invalid JSON")
  204. request.assert_called_once_with(
  205. method="GET",
  206. url=_metadata._METADATA_ROOT + PATH,
  207. headers=_metadata._METADATA_HEADERS,
  208. )
  209. def test_get_project_id():
  210. project = "example-project"
  211. request = make_request(project, headers={"content-type": "text/plain"})
  212. project_id = _metadata.get_project_id(request)
  213. request.assert_called_once_with(
  214. method="GET",
  215. url=_metadata._METADATA_ROOT + "project/project-id",
  216. headers=_metadata._METADATA_HEADERS,
  217. )
  218. assert project_id == project
  219. @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
  220. def test_get_service_account_token(utcnow):
  221. ttl = 500
  222. request = make_request(
  223. json.dumps({"access_token": "token", "expires_in": ttl}),
  224. headers={"content-type": "application/json"},
  225. )
  226. token, expiry = _metadata.get_service_account_token(request)
  227. request.assert_called_once_with(
  228. method="GET",
  229. url=_metadata._METADATA_ROOT + PATH + "/token",
  230. headers=_metadata._METADATA_HEADERS,
  231. )
  232. assert token == "token"
  233. assert expiry == utcnow() + datetime.timedelta(seconds=ttl)
  234. @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
  235. def test_get_service_account_token_with_scopes_list(utcnow):
  236. ttl = 500
  237. request = make_request(
  238. json.dumps({"access_token": "token", "expires_in": ttl}),
  239. headers={"content-type": "application/json"},
  240. )
  241. token, expiry = _metadata.get_service_account_token(request, scopes=["foo", "bar"])
  242. request.assert_called_once_with(
  243. method="GET",
  244. url=_metadata._METADATA_ROOT + PATH + "/token" + "?scopes=foo%2Cbar",
  245. headers=_metadata._METADATA_HEADERS,
  246. )
  247. assert token == "token"
  248. assert expiry == utcnow() + datetime.timedelta(seconds=ttl)
  249. @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
  250. def test_get_service_account_token_with_scopes_string(utcnow):
  251. ttl = 500
  252. request = make_request(
  253. json.dumps({"access_token": "token", "expires_in": ttl}),
  254. headers={"content-type": "application/json"},
  255. )
  256. token, expiry = _metadata.get_service_account_token(request, scopes="foo,bar")
  257. request.assert_called_once_with(
  258. method="GET",
  259. url=_metadata._METADATA_ROOT + PATH + "/token" + "?scopes=foo%2Cbar",
  260. headers=_metadata._METADATA_HEADERS,
  261. )
  262. assert token == "token"
  263. assert expiry == utcnow() + datetime.timedelta(seconds=ttl)
  264. def test_get_service_account_info():
  265. key, value = "foo", "bar"
  266. request = make_request(
  267. json.dumps({key: value}), headers={"content-type": "application/json"}
  268. )
  269. info = _metadata.get_service_account_info(request)
  270. request.assert_called_once_with(
  271. method="GET",
  272. url=_metadata._METADATA_ROOT + PATH + "/?recursive=true",
  273. headers=_metadata._METADATA_HEADERS,
  274. )
  275. assert info[key] == value