Skip to content

Commit

Permalink
better health status for starting and stopping functions nad using pr…
Browse files Browse the repository at this point in the history
…ocess.run to mitigate freezes caused in popen
  • Loading branch information
MRColorR committed Dec 12, 2024
1 parent ed44052 commit 72acadb
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 60 deletions.
47 changes: 29 additions & 18 deletions utils/fn_startStack.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import platform
import subprocess
import time
import threading
from colorama import Fore, Style, just_fix_windows_console

# Ensure the parent directory is in the sys.path
Expand All @@ -20,7 +21,7 @@
from utils.cls import cls
from utils.generator import generate_dashboard_urls
from utils.prompt_helper import ask_question_yn
from utils.helper import is_user_root, is_user_in_docker_group, create_docker_group_if_needed, run_docker_command
from utils.helper import is_user_root, is_user_in_docker_group, create_docker_group_if_needed, run_docker_command, show_spinner

# Global config loading and global variables
m4b_config_path = os.path.join(parent_dir, "config", "m4b-config.json")
Expand Down Expand Up @@ -55,22 +56,29 @@ def start_stack(compose_file: str = './docker-compose.yaml', env_file: str = './
time.sleep(sleep_time)
return False

event = threading.Event()
spinner_thread = threading.Thread(target=show_spinner, args=(f"Starting stack for '{instance_name}'...", event))
spinner_thread.start()

use_sudo = not is_user_root() and platform.system().lower() == 'linux'
try:
command = ["docker", "compose", "-f", compose_file, "--env-file", env_file, "up", "-d", "--remove-orphans"]
exit_code = run_docker_command(command, use_sudo=use_sudo)
if exit_code == 0:
result = run_docker_command(command, use_sudo=use_sudo)
if result == 0:
print(f"{Fore.GREEN}All Apps for '{instance_name}' instance started successfully.{Style.RESET_ALL}")
logging.info(f"Stack for '{instance_name}' started successfully.")
else:
print(f"{Fore.RED}Error starting Docker stack for '{instance_name}' instance. Please check that Docker is running and that the configuration is complete, then try again.{Style.RESET_ALL}")
logging.error(f"Stack for '{instance_name}' failed to start with exit code {exit_code}.")
time.sleep(sleep_time)
return exit_code == 0
logging.error(f"Stack for '{instance_name}' failed to start with exit code {result}.")
time.sleep(sleep_time)
return result == 0
except Exception as e:
print(f"{Fore.RED}An unexpected error occurred while starting the stack for '{instance_name}' instance.{Style.RESET_ALL}")
logging.error(f"Unexpected error: {str(e)}")
time.sleep(sleep_time)
finally:
event.set()
spinner_thread.join()
return False


Expand All @@ -93,18 +101,21 @@ def start_all_stacks(main_compose_file: str = './docker-compose.yaml', main_env_
if platform.system().lower() == 'linux' and not is_user_in_docker_group():
create_docker_group_if_needed()

all_started = start_stack(main_compose_file, main_env_file, main_instance_name, skip_questions=True)
if all_started and os.path.isdir(instances_dir):
for instance in os.listdir(instances_dir):
instance_dir = os.path.join(instances_dir, instance)
compose_file = os.path.join(instance_dir, 'docker-compose.yaml')
env_file = os.path.join(instance_dir, '.env')
if os.path.isfile(compose_file) and os.path.isfile(env_file):
all_started &= start_stack(compose_file, env_file, instance, skip_questions=True)

if all_started:
generate_dashboard_urls(None, None, main_env_file)
print(f"{Fore.YELLOW}Use the previously generated apps nodes URLs to add your device in any apps dashboard that require node claiming/registration (e.g., Earnapp, ProxyRack, etc.){Style.RESET_ALL}")
try:
all_started = start_stack(main_compose_file, main_env_file, main_instance_name, skip_questions=True)
if all_started and os.path.isdir(instances_dir):
for instance in os.listdir(instances_dir):
instance_dir = os.path.join(instances_dir, instance)
compose_file = os.path.join(instance_dir, 'docker-compose.yaml')
env_file = os.path.join(instance_dir, '.env')
if os.path.isfile(compose_file) and os.path.isfile(env_file):
all_started &= start_stack(compose_file, env_file, instance, skip_questions=True)

if all_started:
generate_dashboard_urls(None, None, main_env_file)
print(f"{Fore.YELLOW}Use the previously generated apps nodes URLs to add your device in any apps dashboard that require node claiming/registration (e.g., Earnapp, ProxyRack, etc.){Style.RESET_ALL}")
logging.info("All stacks started successfully.")
finally:
time.sleep(sleep_time)


Expand Down
52 changes: 31 additions & 21 deletions utils/fn_stopStack.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import platform
import time
import threading
from colorama import Fore, Style, just_fix_windows_console

# Ensure the parent directory is in the sys.path
Expand All @@ -17,7 +18,7 @@
from utils import loader
from utils.cls import cls
from utils.prompt_helper import ask_question_yn
from utils.helper import is_user_root, is_user_in_docker_group, create_docker_group_if_needed, run_docker_command
from utils.helper import is_user_root, is_user_in_docker_group, create_docker_group_if_needed, run_docker_command, show_spinner

# Global config loading and global variables
m4b_config_path = os.path.join(parent_dir, "config", "m4b-config.json")
Expand Down Expand Up @@ -51,22 +52,27 @@ def stop_stack(compose_file: str = './docker-compose.yaml', instance_name: str =
time.sleep(sleep_time)
return False

event = threading.Event()
spinner_thread = threading.Thread(target=show_spinner, args=(f"Stopping stack for '{instance_name}'...", event))
spinner_thread.start()

use_sudo = not is_user_root() and platform.system().lower() == 'linux'
try:
command = ["docker", "compose", "-f", compose_file, "down"]
exit_code = run_docker_command(command, use_sudo=use_sudo)
if exit_code == 0:
result = run_docker_command(command, use_sudo=use_sudo)
if result == 0:
print(f"{Fore.GREEN}All Apps for '{instance_name}' instance stopped and stack deleted.{Style.RESET_ALL}")
logging.info(f"Stack for '{instance_name}' stopped successfully.")
else:
print(f"{Fore.RED}Error stopping and deleting Docker stack for '{instance_name}' instance. Please check the configuration and try again.{Style.RESET_ALL}")
logging.error(f"Stack for '{instance_name}' failed to stop with exit code {exit_code}.")
time.sleep(sleep_time)
return exit_code == 0
logging.error(f"Stack for '{instance_name}' failed to stop with exit code {result}.")
return result == 0
except Exception as e:
print(f"{Fore.RED}An unexpected error occurred while stopping the stack for '{instance_name}' instance.{Style.RESET_ALL}")
logging.error(f"Unexpected error: {str(e)}")
time.sleep(sleep_time)
finally:
event.set()
spinner_thread.join()
return False


Expand All @@ -88,20 +94,23 @@ def stop_all_stacks(main_compose_file: str = './docker-compose.yaml', main_insta
if platform.system().lower() == 'linux' and not is_user_in_docker_group():
create_docker_group_if_needed()

stop_stack(main_compose_file, main_instance_name, skip_questions=True)
if os.path.isdir(instances_dir):
print(f"{Fore.YELLOW}Stopping multi-proxy instances...{Style.RESET_ALL}")
for instance in os.listdir(instances_dir):
instance_dir = os.path.join(instances_dir, instance)
compose_file = os.path.join(instance_dir, 'docker-compose.yaml')
if os.path.isfile(compose_file):
try:
stop_stack(compose_file, instance, skip_questions=True)
except Exception as e:
logging.error(f"Failed to stop instance '{instance}': {str(e)}")
print(f"{Fore.GREEN}All multi-proxy instances stopped successfully.{Style.RESET_ALL}")
else:
logging.warning(f"Multi-proxy instances directory '{instances_dir}' does not exist.")
try:
stop_stack(main_compose_file, main_instance_name, skip_questions=True)
if os.path.isdir(instances_dir):
print(f"{Fore.YELLOW}Stopping multi-proxy instances...{Style.RESET_ALL}")
for instance in os.listdir(instances_dir):
instance_dir = os.path.join(instances_dir, instance)
compose_file = os.path.join(instance_dir, 'docker-compose.yaml')
if os.path.isfile(compose_file):
try:
stop_stack(compose_file, instance, skip_questions=True)
except Exception as e:
logging.error(f"Failed to stop instance '{instance}': {str(e)}")
print(f"{Fore.GREEN}All multi-proxy instances stopped successfully.{Style.RESET_ALL}")
else:
logging.warning(f"Multi-proxy instances directory '{instances_dir}' does not exist.")
finally:
time.sleep(sleep_time)


def main(app_config_path: str, m4b_config_path: str, user_config_path: str) -> None:
Expand All @@ -121,6 +130,7 @@ def main(app_config_path: str, m4b_config_path: str, user_config_path: str) -> N
logging.error(f"An unexpected error occurred in main function: {str(e)}")
print(f"{Fore.RED}An unexpected error occurred: {str(e)}{Style.RESET_ALL}")


if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Stop the Docker Compose stack.')
parser.add_argument('--app-config', type=str, required=True, help='Path to app_config JSON file')
Expand Down
31 changes: 10 additions & 21 deletions utils/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def create_docker_group_if_needed():

def run_docker_command(command, use_sudo=False):
"""
Run a Docker command, optionally using sudo, and show real-time output.
Run a Docker command, optionally using sudo, and handle errors gracefully.
Args:
command (list): The Docker command to run.
Expand All @@ -74,28 +74,17 @@ def run_docker_command(command, use_sudo=False):
logging.info(f"Running command: {' '.join(command)}")

try:
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)

# Stream the output in real-time
for line in process.stdout:
print(line)
for line in process.stderr:
print(line)

process.wait()
if process.returncode != 0:
logging.error(f"Command failed with exit code {process.returncode}")
stderr_output = process.stderr.read()
if stderr_output:
logging.error(f"Command error output: {stderr_output}")
return process.returncode
result = subprocess.run(command, capture_output=True, text=True, check=False)
if result.returncode == 0:
logging.info(result.stdout)
else:
logging.error(f"Command failed with exit code {result.returncode}")
logging.error(result.stderr)
print(f"{Fore.RED}Error: {result.stderr.strip()}{Style.RESET_ALL}")
return result.returncode
except Exception as e:
logging.error(f"{Fore.RED}Failed to run command: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}Unexpected error: {e}{Style.RESET_ALL}")
raise RuntimeError(f"Command failed: {e}")


Expand Down

0 comments on commit 72acadb

Please sign in to comment.