diff --git a/src/middlewared/middlewared/plugins/apps/crud.py b/src/middlewared/middlewared/plugins/apps/crud.py index 8450dc8493716..1c79bd8f511b2 100644 --- a/src/middlewared/middlewared/plugins/apps/crud.py +++ b/src/middlewared/middlewared/plugins/apps/crud.py @@ -18,6 +18,7 @@ from .ix_apps.path import get_installed_app_path, get_installed_app_version_path from .ix_apps.query import list_apps from .ix_apps.setup import setup_install_app_dir +from .ix_apps.utils import AppState from .version_utils import get_latest_version_from_app_versions @@ -33,7 +34,7 @@ class Config: 'app_entry', Str('name'), Str('id'), - Str('state', enum=['STOPPED', 'DEPLOYING', 'RUNNING']), + Str('state', enum=[state.value for state in AppState]), Bool('upgrade_available'), Str('human_version'), Str('version'), diff --git a/src/middlewared/middlewared/plugins/apps/ix_apps/query.py b/src/middlewared/middlewared/plugins/apps/ix_apps/query.py index e0cdd959d9447..5dc5e4209b6cc 100644 --- a/src/middlewared/middlewared/plugins/apps/ix_apps/query.py +++ b/src/middlewared/middlewared/plugins/apps/ix_apps/query.py @@ -6,7 +6,7 @@ from .metadata import get_collective_config, get_collective_metadata from .lifecycle import get_current_app_config from .path import get_app_parent_config_path -from .utils import get_app_name_from_project_name, normalize_reference, PROJECT_PREFIX +from .utils import AppState, get_app_name_from_project_name, normalize_reference, PROJECT_PREFIX COMPOSE_SERVICE_KEY: str = 'com.docker.compose.service' @@ -86,12 +86,18 @@ def list_apps( # When we stop docker service and start it again - the containers can be in exited # state which means we need to account for this. state = 'STOPPED' + exited_containers = 0 for container in workloads['container_details']: if container['state'] == 'starting': - state = 'DEPLOYING' + state = AppState.DEPLOYING.value break elif container['state'] == 'running': - state = 'RUNNING' + state = AppState.RUNNING.value + elif container['state'] == 'exited': + exited_containers += 1 + else: + if exited_containers != 0 and exited_containers == len(workloads['container_details']): + state = AppState.CRASHED.value app_metadata = metadata[app_name] active_workloads = get_default_workload_values() if state == 'STOPPED' else workloads @@ -127,7 +133,7 @@ def list_apps( 'name': entry.name, 'id': entry.name, 'active_workloads': get_default_workload_values(), - 'state': 'STOPPED', + 'state': AppState.STOPPED.value, 'upgrade_available': upgrade_available_for_app(train_to_apps_version_mapping, app_metadata), 'image_updates_available': False, **app_metadata | {'portals': normalize_portal_uris(app_metadata['portals'], host_ip)} diff --git a/src/middlewared/middlewared/plugins/apps/ix_apps/utils.py b/src/middlewared/middlewared/plugins/apps/ix_apps/utils.py index 31e868d8743e9..422b579ec2377 100644 --- a/src/middlewared/middlewared/plugins/apps/ix_apps/utils.py +++ b/src/middlewared/middlewared/plugins/apps/ix_apps/utils.py @@ -1,8 +1,17 @@ +import enum + from catalog_reader.library import RE_VERSION # noqa from middlewared.plugins.apps_images.utils import normalize_reference # noqa from middlewared.plugins.apps.schema_utils import CONTEXT_KEY_NAME # noqa from middlewared.plugins.apps.utils import IX_APPS_MOUNT_PATH, PROJECT_PREFIX, run # noqa +class AppState(enum.Enum): + CRASHED = 'CRASHED' + DEPLOYING = 'DEPLOYING' + RUNNING = 'RUNNING' + STOPPED = 'STOPPED' + + def get_app_name_from_project_name(project_name: str) -> str: return project_name[len(PROJECT_PREFIX):] diff --git a/src/middlewared/middlewared/plugins/apps/logs.py b/src/middlewared/middlewared/plugins/apps/logs.py index 3cafc9a4248c5..74646a9ff1818 100644 --- a/src/middlewared/middlewared/plugins/apps/logs.py +++ b/src/middlewared/middlewared/plugins/apps/logs.py @@ -8,6 +8,7 @@ from middlewared.service import CallError from middlewared.validators import Range +from .ix_apps.utils import AppState from .ix_apps.docker.utils import get_docker_client @@ -38,8 +39,8 @@ def __init__(self, *args, **kwargs): def validate_log_args(self, app_name, container_id): app = self.middleware.call_sync('app.get_instance', app_name) - if app['state'] not in ('RUNNING', 'DEPLOYING'): - raise CallError(f'App "{app_name}" is not running') + if AppState(app['state']) not in (AppState.CRASHED, AppState.RUNNING, AppState.DEPLOYING): + raise CallError(f'Unable to retrieve logs of stopped {app_name!r} app') if not any(c['id'] == container_id for c in app['active_workloads']['container_details']): raise CallError(f'Container "{container_id}" not found in app "{app_name}"', errno=errno.ENOENT)