Skip to content

Commit

Permalink
feat(instance): make ready_timeout configurable
Browse files Browse the repository at this point in the history
Add new class variable `ready_timeout` that can be customizable
per cloud. Changed the default to 10m but kept EC2's at 40m.

Also, updated the public function instance.wait() to explicitly
accept an optional ready_timeout arg. If not given, will use
the default value defined for that instance class.
  • Loading branch information
a-dubs committed Jan 17, 2025
1 parent 59a2372 commit 8e029ed
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 19 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1!10.6.1
1!10.7.0
1 change: 1 addition & 0 deletions pycloudlib/ec2/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class EC2Instance(BaseInstance):
"""EC2 backed instance."""

_type = "ec2"
ready_timeout = 40 # To keep backwards compatibility (will lower soon)

def __init__(self, key_pair, client, instance, *, username: Optional[str] = None):
"""Set up instance.
Expand Down
7 changes: 0 additions & 7 deletions pycloudlib/ibm_classic/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,6 @@ def is_started():
logger.info("Instance %s started", self.name)
self._instance = self._vs_manager.get_instance(self.id)

def wait(self, **kwargs):
"""Wait for instance to be up and cloud-init to be complete."""
logger.info("Waiting for instance %s to be ready", self.name)
self._wait_for_instance_start(**kwargs)
self._wait_for_execute(timeout=180)
self._wait_for_cloudinit()

def wait_for_restart(self, old_boot_id):
"""Wait for instance to be restarted and cloud-init to be complete.
Expand Down
26 changes: 19 additions & 7 deletions pycloudlib/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class BaseInstance(ABC):
"""Base instance object."""

_type = "base"
ready_timeout = 10 # Time to wait for instance to be ready after provisioning (in minutes)

def __init__(self, key_pair, username: Optional[str] = None):
"""Set up instance."""
Expand Down Expand Up @@ -160,10 +161,23 @@ def _wait_for_instance_start(self, **kwargs):
detecting when an instance has started through their API.
"""

def wait(self, **kwargs):
"""Wait for instance to be up and cloud-init to be complete."""
def wait(
self,
ready_timeout: Optional[int] = None,
**kwargs,
):
"""
Wait for instance to be up and cloud-init to be complete.
Args:
ready_timeout (int): maximum time to wait for the instance to be ready for ssh after
instance provisioning is complete
Raises:
PycloudlibTimeoutError: If the instance is not ready after the timeout
"""
self._wait_for_instance_start(**kwargs)
self._wait_for_execute()
self._wait_for_execute(timeout=ready_timeout)
self._wait_for_cloudinit()

def wait_for_restart(self, old_boot_id):
Expand Down Expand Up @@ -478,7 +492,7 @@ def _tmpfile(self):
self._tmp_count += 1
return path

def _wait_for_execute(self, old_boot_id=None, timeout: int = 40):
def _wait_for_execute(self, old_boot_id=None, timeout: Optional[int] = None):
"""
Wait until we can execute a command in the instance.
Expand All @@ -492,9 +506,7 @@ def _wait_for_execute(self, old_boot_id=None, timeout: int = 40):
"""
self._log.info("_wait_for_execute to complete")

# Wait 40 minutes before failing. AWS EC2 metal instances can take
# over 20 minutes to start or restart, so we shouldn't lower
# this timeout
timeout = timeout or self.ready_timeout
start = time.time()
end = start + timeout * 60
while time.time() < end:
Expand Down
8 changes: 4 additions & 4 deletions tests/unit_tests/test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ def test_wait_execute_failure(
):
"""Test wait calls when execute command fails."""
instance = concrete_instance_cls(key_pair=None)
m_time.side_effect = [1, 1, 2, 40 * 60, 40 * 60 + 1]
m_time.side_effect = [1, 1, 2, 10 * 60, 10 * 60 + 1]
m_execute.side_effect = execute_effect
expected_msg = "Instance can't be reached after 40 minutes. Failed to obtain new boot id"
expected_msg = "Instance can't be reached after 10 minutes. Failed to obtain new boot id"
expected_call_args = [mock.call("cat /proc/sys/kernel/random/boot_id", no_log=True)] * 2

with pytest.raises(PycloudlibTimeoutError) as excinfo:
Expand Down Expand Up @@ -190,8 +190,8 @@ def test_boot_id_failure(
"""Test wait calls when execute command fails."""
m_execute.side_effect = execute_side_effect
instance = concrete_instance_cls(key_pair=None)
m_time.side_effect = [1, 1, 2, 40 * 60, 40 * 60 + 1]
expected_msg = "Instance can't be reached after 40 minutes. Failed to obtain new boot id"
m_time.side_effect = [1, 1, 2, 10 * 60, 10 * 60 + 1]
expected_msg = "Instance can't be reached after 10 minutes. Failed to obtain new boot id"
expected_call_args = [mock.call("cat /proc/sys/kernel/random/boot_id", no_log=True)] * 2

with pytest.raises(PycloudlibTimeoutError) as excinfo:
Expand Down

0 comments on commit 8e029ed

Please sign in to comment.