summaryrefslogtreecommitdiff
path: root/tests/unittests/sources/azure/test_imds.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unittests/sources/azure/test_imds.py')
-rw-r--r--tests/unittests/sources/azure/test_imds.py282
1 files changed, 214 insertions, 68 deletions
diff --git a/tests/unittests/sources/azure/test_imds.py b/tests/unittests/sources/azure/test_imds.py
index b5a72645..03f66502 100644
--- a/tests/unittests/sources/azure/test_imds.py
+++ b/tests/unittests/sources/azure/test_imds.py
@@ -3,20 +3,30 @@
import json
import logging
import math
+import re
from unittest import mock
import pytest
import requests
from cloudinit.sources.azure import imds
-from cloudinit.url_helper import UrlError
+from cloudinit.url_helper import UrlError, readurl
-MOCKPATH = "cloudinit.sources.azure.imds."
+LOG_PATH = "cloudinit.sources.azure.imds"
+MOCK_PATH = "cloudinit.sources.azure.imds."
+
+
+class StringMatch:
+ def __init__(self, regex) -> None:
+ self.regex = regex
+
+ def __eq__(self, other) -> bool:
+ return bool(re.match("^" + self.regex + "$", other))
@pytest.fixture
-def mock_readurl():
- with mock.patch(MOCKPATH + "readurl", autospec=True) as m:
+def wrapped_readurl():
+ with mock.patch.object(imds, "readurl", wraps=readurl) as m:
yield m
@@ -56,54 +66,63 @@ class TestFetchMetadataWithApiFallback:
def test_basic(
self,
caplog,
- mock_readurl,
+ mock_requests_session_request,
+ wrapped_readurl,
):
fake_md = {"foo": {"bar": []}}
- mock_readurl.side_effect = [
- mock.Mock(contents=json.dumps(fake_md).encode()),
+ mock_requests_session_request.side_effect = [
+ mock.Mock(content=json.dumps(fake_md)),
]
md = imds.fetch_metadata_with_api_fallback()
assert md == fake_md
- assert mock_readurl.mock_calls == [
+ assert wrapped_readurl.mock_calls == [
mock.call(
self.default_url,
timeout=self.timeout,
headers=self.headers,
retries=self.retries,
- exception_cb=imds._readurl_exception_callback,
+ exception_cb=mock.ANY,
infinite=False,
log_req_resp=True,
- ),
+ )
]
-
- warnings = [
- x.message for x in caplog.records if x.levelno == logging.WARNING
+ assert caplog.record_tuples == [
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch("Read from.*"),
+ ),
]
- assert warnings == []
def test_basic_fallback(
self,
caplog,
- mock_readurl,
+ mock_requests_session_request,
+ wrapped_readurl,
):
fake_md = {"foo": {"bar": []}}
- mock_readurl.side_effect = [
+ mock_requests_session_request.side_effect = [
UrlError("No IMDS version", code=400),
- mock.Mock(contents=json.dumps(fake_md).encode()),
+ mock.Mock(content=json.dumps(fake_md)),
]
md = imds.fetch_metadata_with_api_fallback()
assert md == fake_md
- assert mock_readurl.mock_calls == [
+ assert wrapped_readurl.mock_calls == [
mock.call(
self.default_url,
timeout=self.timeout,
headers=self.headers,
retries=self.retries,
- exception_cb=imds._readurl_exception_callback,
+ exception_cb=mock.ANY,
infinite=False,
log_req_resp=True,
),
@@ -112,18 +131,38 @@ class TestFetchMetadataWithApiFallback:
timeout=self.timeout,
headers=self.headers,
retries=self.retries,
- exception_cb=imds._readurl_exception_callback,
+ exception_cb=mock.ANY,
infinite=False,
log_req_resp=True,
),
]
- warnings = [
- x.message for x in caplog.records if x.levelno == logging.WARNING
- ]
- assert warnings == [
- "Failed to fetch metadata from IMDS: No IMDS version",
- "Falling back to IMDS api-version: 2019-06-01",
+ assert caplog.record_tuples == [
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ LOG_PATH,
+ logging.WARNING,
+ "Failed to fetch metadata from IMDS: No IMDS version",
+ ),
+ (
+ LOG_PATH,
+ logging.WARNING,
+ "Falling back to IMDS api-version: 2019-06-01",
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch("Read from.*"),
+ ),
]
@pytest.mark.parametrize(
@@ -155,11 +194,36 @@ class TestFetchMetadataWithApiFallback:
assert md == fake_md
assert len(mock_requests_session_request.mock_calls) == 2
assert mock_url_helper_time_sleep.mock_calls == [mock.call(1)]
-
- warnings = [
- x.message for x in caplog.records if x.levelno == logging.WARNING
+ assert caplog.record_tuples == [
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ LOG_PATH,
+ logging.INFO,
+ StringMatch(
+ "Polling IMDS failed attempt 1 with exception:"
+ f".*{error!s}.*"
+ ),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch("Please wait 1 second.*"),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[1/11\] open.*"),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch("Read from.*"),
+ ),
]
- assert warnings == []
def test_will_retry_errors_on_fallback(
self,
@@ -180,13 +244,58 @@ class TestFetchMetadataWithApiFallback:
assert md == fake_md
assert len(mock_requests_session_request.mock_calls) == 3
assert mock_url_helper_time_sleep.mock_calls == [mock.call(1)]
-
- warnings = [
- x.message for x in caplog.records if x.levelno == logging.WARNING
- ]
- assert warnings == [
- "Failed to fetch metadata from IMDS: fake error",
- "Falling back to IMDS api-version: 2019-06-01",
+ assert caplog.record_tuples == [
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ LOG_PATH,
+ logging.INFO,
+ StringMatch(
+ "Polling IMDS failed attempt 1 with exception:"
+ f".*{error!s}.*"
+ ),
+ ),
+ (
+ LOG_PATH,
+ logging.WARNING,
+ "Failed to fetch metadata from IMDS: fake error",
+ ),
+ (
+ LOG_PATH,
+ logging.WARNING,
+ "Falling back to IMDS api-version: 2019-06-01",
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ LOG_PATH,
+ logging.INFO,
+ StringMatch(
+ "Polling IMDS failed attempt 1 with exception:"
+ f".*{error!s}.*"
+ ),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch("Please wait 1 second.*"),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[1/11\] open.*"),
+ ),
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch("Read from.*"),
+ ),
]
@pytest.mark.parametrize(
@@ -221,10 +330,24 @@ class TestFetchMetadataWithApiFallback:
== [mock.call(1)] * self.retries
)
- warnings = [
- x.message for x in caplog.records if x.levelno == logging.WARNING
+ logs = [x for x in caplog.record_tuples if x[0] == LOG_PATH]
+ assert logs == [
+ (
+ LOG_PATH,
+ logging.INFO,
+ StringMatch(
+ f"Polling IMDS failed attempt {i} with exception:"
+ f".*{error!s}.*"
+ ),
+ )
+ for i in range(1, 12)
+ ] + [
+ (
+ LOG_PATH,
+ logging.WARNING,
+ f"Failed to fetch metadata from IMDS: {error!s}",
+ )
]
- assert warnings == [f"Failed to fetch metadata from IMDS: {error!s}"]
@pytest.mark.parametrize(
"error",
@@ -253,30 +376,47 @@ class TestFetchMetadataWithApiFallback:
assert len(mock_requests_session_request.mock_calls) == 1
assert mock_url_helper_time_sleep.mock_calls == []
- warnings = [
- x.message for x in caplog.records if x.levelno == logging.WARNING
+ assert caplog.record_tuples == [
+ (
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"\[0/11\] open.*"),
+ ),
+ (
+ LOG_PATH,
+ logging.INFO,
+ StringMatch(
+ "Polling IMDS failed attempt 1 with exception:"
+ f".*{error!s}.*"
+ ),
+ ),
+ (
+ LOG_PATH,
+ logging.WARNING,
+ f"Failed to fetch metadata from IMDS: {error!s}",
+ ),
]
- assert warnings == [f"Failed to fetch metadata from IMDS: {error!s}"]
def test_non_json_repsonse(
self,
caplog,
- mock_readurl,
+ mock_requests_session_request,
+ wrapped_readurl,
):
- mock_readurl.side_effect = [
- mock.Mock(contents=b"bad data"),
+ mock_requests_session_request.side_effect = [
+ mock.Mock(content=b"bad data")
]
with pytest.raises(ValueError):
imds.fetch_metadata_with_api_fallback()
- assert mock_readurl.mock_calls == [
+ assert wrapped_readurl.mock_calls == [
mock.call(
self.default_url,
timeout=self.timeout,
headers=self.headers,
retries=self.retries,
- exception_cb=imds._readurl_exception_callback,
+ exception_cb=mock.ANY,
infinite=False,
log_req_resp=True,
),
@@ -304,17 +444,18 @@ class TestFetchReprovisionData:
def test_basic(
self,
caplog,
- mock_readurl,
+ mock_requests_session_request,
+ wrapped_readurl,
):
content = b"ovf content"
- mock_readurl.side_effect = [
- mock.Mock(contents=content),
+ mock_requests_session_request.side_effect = [
+ mock.Mock(content=content),
]
ovf = imds.fetch_reprovision_data()
assert ovf == content
- assert mock_readurl.mock_calls == [
+ assert wrapped_readurl.mock_calls == [
mock.call(
self.url,
timeout=self.timeout,
@@ -327,10 +468,15 @@ class TestFetchReprovisionData:
assert caplog.record_tuples == [
(
- "cloudinit.sources.azure.imds",
+ "cloudinit.url_helper",
+ logging.DEBUG,
+ StringMatch(r"Read from.*"),
+ ),
+ (
+ LOG_PATH,
logging.DEBUG,
"Polled IMDS 1 time(s)",
- )
+ ),
]
@pytest.mark.parametrize(
@@ -370,10 +516,10 @@ class TestFetchReprovisionData:
)
backoff_logs = [
(
- "cloudinit.sources.azure.imds",
+ LOG_PATH,
logging.INFO,
- "Polling IMDS failed with exception: "
- f"{wrapped_error!r} count: {i}",
+ f"Polling IMDS failed attempt {i} with exception: "
+ f"{wrapped_error!r}",
)
for i in range(1, failures + 1)
if i == 1 or math.log2(i).is_integer()
@@ -382,10 +528,10 @@ class TestFetchReprovisionData:
(
"cloudinit.url_helper",
logging.DEBUG,
- mock.ANY,
+ StringMatch(r"Read from.*"),
),
(
- "cloudinit.sources.azure.imds",
+ LOG_PATH,
logging.DEBUG,
f"Polled IMDS {failures+1} time(s)",
),
@@ -437,20 +583,20 @@ class TestFetchReprovisionData:
backoff_logs = [
(
- "cloudinit.sources.azure.imds",
+ LOG_PATH,
logging.INFO,
- "Polling IMDS failed with exception: "
- f"{wrapped_error!r} count: {i}",
+ f"Polling IMDS failed attempt {i} with exception: "
+ f"{wrapped_error!r}",
)
for i in range(1, failures + 1)
if i == 1 or math.log2(i).is_integer()
]
assert caplog.record_tuples == backoff_logs + [
(
- "cloudinit.sources.azure.imds",
+ LOG_PATH,
logging.INFO,
- "Polling IMDS failed with exception: "
- f"{exc_info.value!r} count: {failures+1}",
+ f"Polling IMDS failed attempt {failures+1} with exception: "
+ f"{exc_info.value!r}",
),
]
@@ -483,9 +629,9 @@ class TestFetchReprovisionData:
assert caplog.record_tuples == [
(
- "cloudinit.sources.azure.imds",
+ LOG_PATH,
logging.INFO,
- "Polling IMDS failed with exception: "
- f"{exc_info.value!r} count: 1",
+ "Polling IMDS failed attempt 1 with exception: "
+ f"{exc_info.value!r}",
),
]