Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion libcloud/test/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
from libcloud.test import unittest, no_internet
from libcloud.utils.py3 import assertRaisesRegex
from libcloud.common.base import Response, Connection, CertificateConnection
from libcloud.utils.retry import RETRY_EXCEPTIONS, Retry, RetryForeverOnRateLimitError
from libcloud.utils.retry import (
RETRY_EXCEPTIONS,
Retry,
MinimalRetry,
RetryForeverOnRateLimitError,
)
from libcloud.common.exceptions import RateLimitReachedError


Expand Down Expand Up @@ -558,6 +563,32 @@ def responseCls(connection, response) -> mock.MagicMock:
def _raise_socket_error(self):
raise socket.gaierror("")

def test_retry_with_nonpositive_timeout_returns_first_result(self):
for retry_class in (MinimalRetry, Retry):
for timeout in (0, -1):
with self.subTest(retry_class=retry_class, timeout=timeout):
request = Mock(return_value="success")
result = retry_class(timeout=timeout)(request)("/", method="GET")
self.assertEqual(result, "success")
request.assert_called_once_with("/", method="GET")

@patch("libcloud.utils.retry.time.sleep")
def test_retry_with_nonpositive_timeout_preserves_first_error(self, sleep):
for retry_class in (MinimalRetry, Retry):
for timeout in (0, -1):
for error in (
socket.gaierror("unavailable"),
RateLimitReachedError(),
ValueError(),
):
with self.subTest(retry_class=retry_class, timeout=timeout, error=error):
request = Mock(side_effect=error)
with self.assertRaises(type(error)) as exc:
retry_class(timeout=timeout)(request)()
self.assertIs(exc.exception, error)
request.assert_called_once_with()
sleep.assert_not_called()

@patch("libcloud.common.base.Connection.request")
def test_retry_with_sleep(self, mock_connect):
con = Connection()
Expand Down
5 changes: 4 additions & 1 deletion libcloud/utils/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,15 @@ def retry_loop(*args, **kwargs):
end = datetime.now() + timedelta(seconds=self.timeout)
last_exc = None

while datetime.now() < end:
while last_exc is None or datetime.now() < end:
try:
return transform_ssl_error(func, *args, **kwargs)
except Exception as exc:
last_exc = exc

if datetime.now() >= end:
raise

if isinstance(exc, RateLimitReachedError):
_logger.debug("You are being rate limited, backing " "off...")

Expand Down