Skip to content

Commit ed62fd5

Browse files
authored
Merge pull request #723 from cderici/wait-exact-units-zero
#723 #### Description This adds logic to wait for `wait_for_exact_units=0` option for `model.wait_for_idle()`. Fixes: #721 #### QA Steps ``` tox -e integration -- tests/integration/test_model.py::test_wait_for_idle_with_exact_units_scale_down_zero ```
2 parents fd94f06 + 28a95aa commit ed62fd5

2 files changed

Lines changed: 41 additions & 9 deletions

File tree

juju/model.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,7 +2532,7 @@ async def _get_source_api(self, url, controller_name=None):
25322532

25332533
async def wait_for_idle(self, apps=None, raise_on_error=True, raise_on_blocked=False,
25342534
wait_for_active=False, timeout=10 * 60, idle_period=15, check_freq=0.5,
2535-
status=None, wait_for_units=1, wait_for_exact_units=-1):
2535+
status=None, wait_for_units=1, wait_for_exact_units=None):
25362536
"""Wait for applications in the model to settle into an idle state.
25372537
25382538
:param apps (list[str]): Optional list of specific app names to wait on.
@@ -2574,8 +2574,7 @@ async def wait_for_idle(self, apps=None, raise_on_error=True, raise_on_blocked=F
25742574
25752575
:param wait_for_exact_units (int): The exact number of units to be expected before
25762576
going into the idle state. (e.g. useful for scaling down).
2577-
The default is -1 unit.
2578-
When positive, takes precedence over the `wait_for_units` parameter.
2577+
When set, takes precedence over the `wait_for_units` parameter.
25792578
"""
25802579
if wait_for_active:
25812580
warnings.warn("wait_for_active is deprecated; use status", DeprecationWarning)
@@ -2606,6 +2605,10 @@ def _raise_for_status(entities, status):
26062605
", ".join(errored),
26072606
))
26082607

2608+
if wait_for_exact_units is not None:
2609+
assert type(wait_for_exact_units) == int and wait_for_exact_units >= 0, \
2610+
'Invalid value for wait_for_exact_units : %s' % wait_for_exact_units
2611+
26092612
while True:
26102613
busy = []
26112614
errors = {}
@@ -2619,7 +2622,7 @@ def _raise_for_status(entities, status):
26192622
errors.setdefault("App", []).append(app.name)
26202623
if raise_on_blocked and app.status == "blocked":
26212624
blocks.setdefault("App", []).append(app.name)
2622-
if wait_for_exact_units > 0:
2625+
if wait_for_exact_units is not None:
26232626
if len(app.units) != wait_for_exact_units:
26242627
busy.append(app.name + " (waiting for exactly %s units, current : %s)" %
26252628
(wait_for_exact_units, len(app.units)))

tests/integration/test_model.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,8 @@ async def add_manual_machine_ssh(event_loop, is_root=False):
457457

458458
def wait_for_network(container, timeout=30):
459459
"""Wait for eth0 to have an ipv4 address."""
460-
starttime = time.time()
461-
while time.time() < starttime + timeout:
460+
starttime = time.perf_counter()
461+
while time.perf_counter() < starttime + timeout:
462462
time.sleep(1)
463463
if 'eth0' in container.state().network:
464464
addresses = container.state().network['eth0']['addresses']
@@ -835,11 +835,40 @@ async def test_wait_for_idle_with_exact_units_scale_down(event_loop):
835835
await app.destroy_units(*two_units_to_remove)
836836

837837
# assert that the following wait is not returning instantaneously
838-
starttime = time.time()
838+
start_time = time.perf_counter()
839839
await model.wait_for_idle(timeout=5 * 60, wait_for_exact_units=1)
840-
endtime = time.time()
840+
end_time = time.perf_counter()
841841
# checking if waited more than 10ms
842-
assert (endtime - starttime) > 0.001
842+
assert (end_time - start_time) > 0.001
843+
844+
845+
@base.bootstrapped
846+
@pytest.mark.asyncio
847+
async def test_wait_for_idle_with_exact_units_scale_down_zero(event_loop):
848+
"""Deploys 3 units, waits for them to be idle, then removes 3 of them,
849+
then waits for exactly 0 unit to be left.
850+
851+
"""
852+
async with base.CleanModel() as model:
853+
app = await model.deploy(
854+
'ubuntu',
855+
application_name='ubuntu',
856+
series='bionic',
857+
channel='stable',
858+
num_units=3,
859+
)
860+
await model.wait_for_idle(timeout=5 * 60, wait_for_exact_units=3)
861+
862+
units_to_remove = [u.name for u in app.units]
863+
# Remove all the units
864+
await app.destroy_units(*units_to_remove)
865+
866+
# assert that the following wait is not returning instantaneously
867+
start_time = time.perf_counter()
868+
await model.wait_for_idle(timeout=5 * 60, wait_for_exact_units=0)
869+
end_time = time.perf_counter()
870+
# checking if waited more than 10ms
871+
assert (end_time - start_time) > 0.001
843872

844873

845874
@base.bootstrapped

0 commit comments

Comments
 (0)