Add an `application_ready` function in the Juju Watcher to check if all the
applications of the model are ready. This function checks that not only
the application is ready, but also all the units of the application.
Additionally, I have added a stability check. After considering a model
as ready, we will wait for 10 seconds, and check again.
Change-Id: Icd8d88613416b75cfd241e7044dbeffa6cbc6a8e
Signed-off-by: David Garcia <david.garcia@canonical.com>
def entity_ready(entity: ModelEntity) -> bool:
def entity_ready(entity: ModelEntity) -> bool:
+ """
+ Check if the entity is ready
+
+ :param: entity: Model entity. It can be a machine, action, or application.
+
+ :returns: boolean saying if the entity is ready or not
+ """
entity_type = entity.entity_type
if entity_type == "machine":
return entity.agent_status in ["started"]
entity_type = entity.entity_type
if entity_type == "machine":
return entity.agent_status in ["started"]
return entity.status in ["completed", "failed", "cancelled"]
elif entity_type == "application":
# Workaround for bug: https://github.com/juju/python-libjuju/issues/441
return entity.status in ["completed", "failed", "cancelled"]
elif entity_type == "application":
# Workaround for bug: https://github.com/juju/python-libjuju/issues/441
- return status(entity) in ["active", "blocked"]
+ return entity.status in ["active", "blocked"]
else:
raise EntityInvalidException("Unknown entity type: {}".format(entity_type))
else:
raise EntityInvalidException("Unknown entity type: {}".format(entity_type))
+def application_ready(application: Application) -> bool:
+ """
+ Check if an application has a leader
+
+ :param: application: Application entity.
+
+ :returns: boolean saying if the application has a unit that is a leader.
+ """
+ ready_status_list = ["active", "blocked"]
+ application_ready = application.status in ready_status_list
+ units_ready = all(
+ unit.workload_status in ready_status_list for unit in application.units
+ )
+ return application_ready and units_ready
+
+
class JujuModelWatcher:
@staticmethod
async def wait_for_model(model: Model, timeout: float = 3600):
class JujuModelWatcher:
@staticmethod
async def wait_for_model(model: Model, timeout: float = 3600):
timeout = 3600.0
# Coroutine to wait until the entity reaches the final state
timeout = 3600.0
# Coroutine to wait until the entity reaches the final state
- wait_for_entity = asyncio.ensure_future(
- asyncio.wait_for(
- model.block_until(
- lambda: all(
- entity_ready(entity) for entity in model.applications.values()
- )
- ),
- timeout=timeout,
+ async def wait_until_model_ready():
+ wait_for_entity = asyncio.ensure_future(
+ asyncio.wait_for(
+ model.block_until(
+ lambda: all(
+ application_ready(application)
+ for application in model.applications.values()
+ ),
+ ),
+ timeout=timeout,
+ )
- tasks = [wait_for_entity]
- try:
- await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
- finally:
- # Cancel tasks
- for task in tasks:
- task.cancel()
+ tasks = [wait_for_entity]
+ try:
+ await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
+ finally:
+ # Cancel tasks
+ for task in tasks:
+ task.cancel()
+
+ await wait_until_model_ready()
+ # Check model is still ready after 10 seconds
+
+ await asyncio.sleep(10)
+ await wait_until_model_ready()
@staticmethod
async def wait_for(
@staticmethod
async def wait_for(
# Coroutine to wait until the entity reaches the final state
wait_for_entity = asyncio.ensure_future(
asyncio.wait_for(
# Coroutine to wait until the entity reaches the final state
wait_for_entity = asyncio.ensure_future(
asyncio.wait_for(
- model.block_until(lambda: entity_ready(entity)), timeout=total_timeout,
+ model.block_until(lambda: entity_ready(entity)),
+ timeout=total_timeout,