diff --git a/frappe_manager/commands.py b/frappe_manager/commands.py index 8fb1418a..dcb3f563 100644 --- a/frappe_manager/commands.py +++ b/frappe_manager/commands.py @@ -105,35 +105,38 @@ def app_callback( fm_config_manager: FMConfigManager = FMConfigManager.import_from_toml() + # docker pull if first_time_install: if not fm_config_manager.root_path.exists(): - richprint.print('🔍 It seems like the first installation. Pulling images... 🖼️') + richprint.print("It seems like the first installation. Pulling docker images...️", "🔍") + completed_status = pull_docker_images() if not completed_status: shutil.rmtree(CLI_DIR) richprint.exit("Aborting. [bold][blue]fm[/blue][/bold] will not be able to work without images. 🖼️") + current_version = Version(get_current_fm_version()) fm_config_manager.version = current_version fm_config_manager.export_to_toml() migrations = MigrationExecutor(fm_config_manager) migration_status = migrations.execute() + if not migration_status: richprint.exit(f"Rollbacked to previous version of fm {migrations.prev_version}.") services_manager: ServicesManager = ServicesManager(verbose=verbose) + services_manager.set_typer_context(ctx) + services_manager.init() + try: - services_manager.entrypoint_checks() + services_manager.entrypoint_checks(start=True) except ServicesNotCreated as e: services_manager.remove_itself() richprint.exit(f"Not able to create services. {e}") - if not services_manager.compose_project.running: - services_manager.are_ports_free() - services_manager.compose_project.start_service() - ctx.obj["services"] = services_manager ctx.obj["verbose"] = verbose ctx.obj['fm_config_manager'] = fm_config_manager diff --git a/frappe_manager/services_manager/__init__.py b/frappe_manager/services_manager/__init__.py index fc0a03ed..675874ac 100644 --- a/frappe_manager/services_manager/__init__.py +++ b/frappe_manager/services_manager/__init__.py @@ -3,3 +3,4 @@ class ServicesEnum(str, Enum): global_db= "global-db" global_nginx_proxy="global-nginx-proxy" + all="all" diff --git a/frappe_manager/services_manager/commands.py b/frappe_manager/services_manager/commands.py index f9de8422..55779af6 100644 --- a/frappe_manager/services_manager/commands.py +++ b/frappe_manager/services_manager/commands.py @@ -9,44 +9,79 @@ @services_root_command.command(no_args_is_help=True) def stop( ctx: typer.Context, - service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the services_manager.")], + service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the service.")], ): """Stops global services.""" services_manager: ServicesManager = ctx.obj["services"] + if service_name.value == ServicesEnum.all: + for service in ServicesEnum: + if service == ServicesEnum.all: + continue - if services_manager.compose_project.is_service_running(service_name.value): - services_manager.compose_project.stop_service(services=[service_name.value]) + if not services_manager.compose_project.is_service_running(service.value): + richprint.print(f"Skipping not running service {service.value}.") + continue + + services_manager.compose_project.stop_service(services=[service.value]) + richprint.print(f"Stopped service {service.value}.") else: - richprint.exit(f"{service_name.value} is not running.") + if services_manager.compose_project.is_service_running(service_name.value): + services_manager.compose_project.stop_service(services=[service_name.value]) + else: + richprint.print(f"Skipping already stopped service {service_name.value}.") @services_root_command.command(no_args_is_help=True) def start( ctx: typer.Context, - service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the services_manager.")], + service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the service.")], ): """Starts global services.""" services_manager: ServicesManager = ctx.obj["services"] - if services_manager.compose_project.is_service_running(service_name.value): - services_manager.compose_project.start_service(services=[service_name.value]) + if service_name.value == ServicesEnum.all: + + for service in ServicesEnum: + if service == ServicesEnum.all: + continue + + if services_manager.compose_project.is_service_running(service.value): + richprint.print(f"Skipping already running service {service.value}.") + continue + + services_manager.compose_project.start_service(services=[service.value]) + richprint.print(f"Started service {service.value}.") else: - richprint.exit(f"{service_name.value} is already running.") + if not services_manager.compose_project.is_service_running(service_name.value): + services_manager.compose_project.start_service(services=[service_name.value]) + else: + richprint.print(f"Skipping already running service {service_name.value}.") @services_root_command.command(no_args_is_help=True) def restart( ctx: typer.Context, - service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the services_manager.")], + service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the service.")], ): """Restarts global services.""" services_manager: ServicesManager = ctx.obj["services"] - services_manager.compose_project.restart_service(services=[service_name.value]) + + if service_name.value == ServicesEnum.all: + + for service in ServicesEnum: + if service == ServicesEnum.all: + continue + + services_manager.compose_project.restart_service(services=[service.value]) + richprint.print(f"Restarted service {service.value}.") + else: + services_manager.compose_project.restart_service(services=[service_name.value]) + richprint.print(f"Restarted service {service_name.value}.") @services_root_command.command(no_args_is_help=True) def shell( ctx: typer.Context, - service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the services_manager.")], + service_name: Annotated[ServicesEnum, typer.Argument(help="Name of the service.")], user: Annotated[Optional[str], typer.Option(help="Connect as this user.")] = None, ): """ diff --git a/frappe_manager/services_manager/services.py b/frappe_manager/services_manager/services.py index 605962ef..ee0c0d04 100644 --- a/frappe_manager/services_manager/services.py +++ b/frappe_manager/services_manager/services.py @@ -47,29 +47,34 @@ def __init__( def entrypoint_checks(self, start=False): if not self.path.exists(): try: - richprint.print(f"Creating services", emoji_code=":construction:") + richprint.print(f"Creating global services [blue]{', '.join(self.compose_project.compose_file_manager.get_services_list())}[/blue].", emoji_code=":construction:") self.path.mkdir(parents=True, exist_ok=True) self.create(clean_install=True) + except Exception as e: - raise ServicesNotCreated(f'Error Caused: {e}') + raise ServicesNotCreated(f"Not able to create global services [blue]{', '.join(self.compose_project.compose_file_manager.get_services_list())}[/blue].") self.compose_project.pull_images() - richprint.print(f"Creating services: Done") + richprint.print(f"Created global services [blue]{', '.join(self.compose_project.compose_file_manager.get_services_list())}[/blue].") + if start: self.compose_project.start_service() if not self.compose_path.exists(): raise ServicesComposeNotExist( - f"Seems like services has taken a down. Compose file not found at -> {self.compose_path}. Please recreate services." + f"Seems like global services has taken a down. No compose file found at {self.compose_path}." ) if start: if not self.typer_context.invoked_subcommand == "service": + if not self.compose_project.running: - richprint.warning("services are not running. Starting it") + self.are_ports_free() + richprint.print(f"Started non running global services [blue]{', '.join(self.compose_project.compose_file_manager.get_services_list())}[/blue].") self.compose_project.start_service() + self.database_manager: DatabaseServiceManager = MariaDBManager( DatabaseServerServiceInfo.import_from_compose_file('global-db', self.compose_project), self.compose_project ) @@ -176,7 +181,7 @@ def create(self, backup: bool = False, clean_install: bool = True): try: temp_dir.mkdir(parents=True, exist_ok=True) except Exception as e: - richprint.exit(f"Failed to create global services bind mount directories. Error: {e}") + raise ServicesNotCreated(f"Failed to create global services required dir {temp_dir.absolute()}.") # populate secrets for db db_password_path = self.path / 'secrets' / 'db_password.txt' @@ -233,7 +238,7 @@ def generate_compose(self, inputs: dict): # TODO do something about this exception except Exception as e: - richprint.exit(f"Not able to generate global site compose. Error: {e}") + raise ServicesNotCreated(f"Not able to generate global services compose file.") def shell(self, container: str, user: str | None = None): richprint.stop() @@ -250,5 +255,8 @@ def remove_itself(self): shutil.rmtree(self.path) def are_ports_free(self): + ports = [80, 443] + richprint.change_head(f"Verifying ports {', '.join(map(str, ports))} availability.") docker_used_ports = self.compose_project.get_host_port_binds() - check_and_display_port_status([80, 443], exclude=docker_used_ports) + check_and_display_port_status(ports, exclude=docker_used_ports) + richprint.print(f"Global services will utilize ports {', '.join(map(str, ports))}.") diff --git a/frappe_manager/utils/helpers.py b/frappe_manager/utils/helpers.py index 28da2adb..32d30c44 100644 --- a/frappe_manager/utils/helpers.py +++ b/frappe_manager/utils/helpers.py @@ -110,7 +110,6 @@ def check_and_display_port_status(ports_to_check: list, exclude=[]): ports_to_check (list): List of ports to check. exclude (list, optional): List of ports to exclude from checking. Defaults to []. """ - richprint.change_head("Checking Ports") if exclude: # Removing elements present in remove_array from original_array ports_to_check = [x for x in exclude if x not in ports_to_check] @@ -119,10 +118,8 @@ def check_and_display_port_status(ports_to_check: list, exclude=[]): already_binded = check_ports(ports_to_check) if already_binded: richprint.exit( - f"Whoa there! Looks like the {' '.join([ str(x) for x in already_binded ])} { 'ports are' if len(already_binded) > 1 else 'port is' } having a party already! Can you do us a solid and free up those ports?" + f"Ports {', '.join(map(str, already_binded))} {'are' if len(already_binded) > 1 else 'is'} currently in use. Please free up these ports." ) - richprint.print("Ports Check : Passed") - def generate_random_text(length=50): """