374 lines
11 KiB
Python
374 lines
11 KiB
Python
# 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
|