| # Copyright 2016 Google LLC |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import datetime |
| import json |
| import os |
| |
| import mock |
| import pytest |
| from six.moves import http_client |
| from six.moves import reload_module |
| |
| from google.auth import _helpers |
| from google.auth import environment_vars |
| from google.auth import exceptions |
| from google.auth import transport |
| from google.auth.compute_engine import _metadata |
| |
| PATH = "instance/service-accounts/default" |
| |
| |
| def make_request(data, status=http_client.OK, headers=None, retry=False): |
| response = mock.create_autospec(transport.Response, instance=True) |
| response.status = status |
| response.data = _helpers.to_bytes(data) |
| response.headers = headers or {} |
| |
| request = mock.create_autospec(transport.Request) |
| if retry: |
| request.side_effect = [exceptions.TransportError(), response] |
| else: |
| request.return_value = response |
| |
| return request |
| |
| |
| def test_ping_success(): |
| request = make_request("", headers=_metadata._METADATA_HEADERS) |
| |
| assert _metadata.ping(request) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_IP_ROOT, |
| headers=_metadata._METADATA_HEADERS, |
| timeout=_metadata._METADATA_DEFAULT_TIMEOUT, |
| ) |
| |
| |
| def test_ping_success_retry(): |
| request = make_request("", headers=_metadata._METADATA_HEADERS, retry=True) |
| |
| assert _metadata.ping(request) |
| |
| request.assert_called_with( |
| method="GET", |
| url=_metadata._METADATA_IP_ROOT, |
| headers=_metadata._METADATA_HEADERS, |
| timeout=_metadata._METADATA_DEFAULT_TIMEOUT, |
| ) |
| assert request.call_count == 2 |
| |
| |
| def test_ping_failure_bad_flavor(): |
| request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"}) |
| |
| assert not _metadata.ping(request) |
| |
| |
| def test_ping_failure_connection_failed(): |
| request = make_request("") |
| request.side_effect = exceptions.TransportError() |
| |
| assert not _metadata.ping(request) |
| |
| |
| def test_ping_success_custom_root(): |
| request = make_request("", headers=_metadata._METADATA_HEADERS) |
| |
| fake_ip = "1.2.3.4" |
| os.environ[environment_vars.GCE_METADATA_IP] = fake_ip |
| reload_module(_metadata) |
| |
| try: |
| assert _metadata.ping(request) |
| finally: |
| del os.environ[environment_vars.GCE_METADATA_IP] |
| reload_module(_metadata) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url="http://" + fake_ip, |
| headers=_metadata._METADATA_HEADERS, |
| timeout=_metadata._METADATA_DEFAULT_TIMEOUT, |
| ) |
| |
| |
| def test_get_success_json(): |
| key, value = "foo", "bar" |
| |
| data = json.dumps({key: value}) |
| request = make_request(data, headers={"content-type": "application/json"}) |
| |
| result = _metadata.get(request, PATH) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH, |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert result[key] == value |
| |
| |
| def test_get_success_retry(): |
| key, value = "foo", "bar" |
| |
| data = json.dumps({key: value}) |
| request = make_request( |
| data, headers={"content-type": "application/json"}, retry=True |
| ) |
| |
| result = _metadata.get(request, PATH) |
| |
| request.assert_called_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH, |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert request.call_count == 2 |
| assert result[key] == value |
| |
| |
| def test_get_success_text(): |
| data = "foobar" |
| request = make_request(data, headers={"content-type": "text/plain"}) |
| |
| result = _metadata.get(request, PATH) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH, |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert result == data |
| |
| |
| def test_get_success_params(): |
| data = "foobar" |
| request = make_request(data, headers={"content-type": "text/plain"}) |
| params = {"recursive": "true"} |
| |
| result = _metadata.get(request, PATH, params=params) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "?recursive=true", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert result == data |
| |
| |
| def test_get_success_recursive_and_params(): |
| data = "foobar" |
| request = make_request(data, headers={"content-type": "text/plain"}) |
| params = {"recursive": "false"} |
| result = _metadata.get(request, PATH, recursive=True, params=params) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "?recursive=true", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert result == data |
| |
| |
| def test_get_success_recursive(): |
| data = "foobar" |
| request = make_request(data, headers={"content-type": "text/plain"}) |
| |
| result = _metadata.get(request, PATH, recursive=True) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "?recursive=true", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert result == data |
| |
| |
| def test_get_success_custom_root_new_variable(): |
| request = make_request("{}", headers={"content-type": "application/json"}) |
| |
| fake_root = "another.metadata.service" |
| os.environ[environment_vars.GCE_METADATA_HOST] = fake_root |
| reload_module(_metadata) |
| |
| try: |
| _metadata.get(request, PATH) |
| finally: |
| del os.environ[environment_vars.GCE_METADATA_HOST] |
| reload_module(_metadata) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH), |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| |
| |
| def test_get_success_custom_root_old_variable(): |
| request = make_request("{}", headers={"content-type": "application/json"}) |
| |
| fake_root = "another.metadata.service" |
| os.environ[environment_vars.GCE_METADATA_ROOT] = fake_root |
| reload_module(_metadata) |
| |
| try: |
| _metadata.get(request, PATH) |
| finally: |
| del os.environ[environment_vars.GCE_METADATA_ROOT] |
| reload_module(_metadata) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH), |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| |
| |
| def test_get_failure(): |
| request = make_request("Metadata error", status=http_client.NOT_FOUND) |
| |
| with pytest.raises(exceptions.TransportError) as excinfo: |
| _metadata.get(request, PATH) |
| |
| assert excinfo.match(r"Metadata error") |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH, |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| |
| |
| def test_get_failure_connection_failed(): |
| request = make_request("") |
| request.side_effect = exceptions.TransportError() |
| |
| with pytest.raises(exceptions.TransportError) as excinfo: |
| _metadata.get(request, PATH) |
| |
| assert excinfo.match(r"Compute Engine Metadata server unavailable") |
| |
| request.assert_called_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH, |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert request.call_count == 5 |
| |
| |
| def test_get_failure_bad_json(): |
| request = make_request("{", headers={"content-type": "application/json"}) |
| |
| with pytest.raises(exceptions.TransportError) as excinfo: |
| _metadata.get(request, PATH) |
| |
| assert excinfo.match(r"invalid JSON") |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH, |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| |
| |
| def test_get_project_id(): |
| project = "example-project" |
| request = make_request(project, headers={"content-type": "text/plain"}) |
| |
| project_id = _metadata.get_project_id(request) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + "project/project-id", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert project_id == project |
| |
| |
| @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) |
| def test_get_service_account_token(utcnow): |
| ttl = 500 |
| request = make_request( |
| json.dumps({"access_token": "token", "expires_in": ttl}), |
| headers={"content-type": "application/json"}, |
| ) |
| |
| token, expiry = _metadata.get_service_account_token(request) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "/token", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert token == "token" |
| assert expiry == utcnow() + datetime.timedelta(seconds=ttl) |
| |
| |
| @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) |
| def test_get_service_account_token_with_scopes_list(utcnow): |
| ttl = 500 |
| request = make_request( |
| json.dumps({"access_token": "token", "expires_in": ttl}), |
| headers={"content-type": "application/json"}, |
| ) |
| |
| token, expiry = _metadata.get_service_account_token(request, scopes=["foo", "bar"]) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "/token" + "?scopes=foo%2Cbar", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert token == "token" |
| assert expiry == utcnow() + datetime.timedelta(seconds=ttl) |
| |
| |
| @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) |
| def test_get_service_account_token_with_scopes_string(utcnow): |
| ttl = 500 |
| request = make_request( |
| json.dumps({"access_token": "token", "expires_in": ttl}), |
| headers={"content-type": "application/json"}, |
| ) |
| |
| token, expiry = _metadata.get_service_account_token(request, scopes="foo,bar") |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "/token" + "?scopes=foo%2Cbar", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| assert token == "token" |
| assert expiry == utcnow() + datetime.timedelta(seconds=ttl) |
| |
| |
| def test_get_service_account_info(): |
| key, value = "foo", "bar" |
| request = make_request( |
| json.dumps({key: value}), headers={"content-type": "application/json"} |
| ) |
| |
| info = _metadata.get_service_account_info(request) |
| |
| request.assert_called_once_with( |
| method="GET", |
| url=_metadata._METADATA_ROOT + PATH + "/?recursive=true", |
| headers=_metadata._METADATA_HEADERS, |
| ) |
| |
| assert info[key] == value |