Skip to content

Commit

Permalink
Pass error messages to the upper level
Browse files Browse the repository at this point in the history
  • Loading branch information
TaekyungHeo committed Jun 3, 2024
1 parent bfc179f commit 44cd218
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 91 deletions.
19 changes: 11 additions & 8 deletions src/cloudai/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,17 @@ def handle_install_and_uninstall(args: argparse.Namespace) -> None:

if args.mode == "install":
logging.info("Installing test templates.")
if installer.is_installed(test_templates):
print("Cloud AI is already installed.")
else:
installer.install(test_templates)
print("Installation completed.")
result = installer.install(test_templates)
if not result:
print(result)
sys.exit(1)

elif args.mode == "uninstall":
logging.info("Uninstalling test templates.")
installer.uninstall(test_templates)
print("Uninstallation completed.")
result = installer.uninstall(test_templates)
if not result:
print(result)
sys.exit(1)


def handle_dry_run_and_run(args: argparse.Namespace) -> None:
Expand Down Expand Up @@ -169,7 +170,9 @@ def handle_dry_run_and_run(args: argparse.Namespace) -> None:
if args.mode == "run":
logging.info("Checking if test templates are installed.")
installer = Installer(system)
if not installer.is_installed(test_templates):
result = installer.is_installed(test_templates)
if not result:
print(result)
print("Cloud AI has not been installed. Please run install mode first.")
sys.exit(1)

Expand Down
88 changes: 66 additions & 22 deletions src/cloudai/_core/base_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Iterable

from cloudai._core.install_status_result import InstallStatusResult

from .system import System
from .test_template import TestTemplate

Expand Down Expand Up @@ -57,60 +59,102 @@ def _is_binary_installed(self, binary_name: str) -> bool:
self.logger.debug(f"Checking if binary '{binary_name}' is installed.")
return shutil.which(binary_name) is not None

def _check_prerequisites(self) -> None:
def _check_prerequisites(self) -> InstallStatusResult:
"""
Check if common prerequisites are installed.
This method should be overridden in derived classes for specific prerequisite checks.
Raises
EnvironmentError: If a required binary is not installed.
Returns
InstallStatusResult: Result containing the status and any error message.
"""
self.logger.info("Checking for common prerequisites.")
return InstallStatusResult(True)

def is_installed(self, test_templates: Iterable[TestTemplate]) -> bool:
def is_installed(self, test_templates: Iterable[TestTemplate]) -> InstallStatusResult:
"""
Check if the necessary components for the provided test templates are already installed.
Verify the installation status of each test template.
Args:
test_templates (Iterable[TestTemplate]): The list of test templates to
check for installation.
test_templates (Iterable[TestTemplate]): The list of test templates to check for installation.
Returns:
bool: True if all test templates are installed, False otherwise.
InstallStatusResult: Result containing the installation status and error message if not installed.
"""
self.logger.info("Verifying installation status of test templates.")
return all(test_template.is_installed() for test_template in test_templates)

def install(self, test_templates: Iterable[TestTemplate]) -> None:
not_installed = {}
for test_template in test_templates:
try:
if not test_template.is_installed():
not_installed[test_template.name] = "Not installed"
except Exception as e:
not_installed[test_template.name] = str(e)

if not_installed:
return InstallStatusResult(False, "Some test templates are not installed.", not_installed)
else:
return InstallStatusResult(True, "All test templates are installed.")

def install(self, test_templates: Iterable[TestTemplate]) -> InstallStatusResult:
"""
Install the necessary components if they are not already installed.
Raises an exception if installation fails for any component.
Args:
test_templates (Iterable[TestTemplate]): The test templates.
Returns:
InstallStatusResult: Result containing the installation status and error message if any.
"""
self.logger.info("Starting installation of test templates.")
self._check_prerequisites()
prerequisites_result = self._check_prerequisites()
if not prerequisites_result:
return prerequisites_result

install_results = {}
with ThreadPoolExecutor() as executor:
futures = [executor.submit(test_template.install) for test_template in test_templates]
futures = {executor.submit(test_template.install): test_template for test_template in test_templates}
for future in as_completed(futures):
future.result()

def uninstall(self, test_templates: Iterable[TestTemplate]) -> None:
test_template = futures[future]
try:
future.result()
install_results[test_template.name] = "Success"
except Exception as e:
self.logger.error(f"Installation failed for {test_template.name}: {e}")
install_results[test_template.name] = str(e)

all_success = all(result == "Success" for result in install_results.values())
if all_success:
return InstallStatusResult(True, "All test templates installed successfully.")
else:
return InstallStatusResult(False, "Some test templates failed to install.", install_results)

def uninstall(self, test_templates: Iterable[TestTemplate]) -> InstallStatusResult:
"""
Uninstalls the benchmarks or test templates.
Raises an exception if uninstallation fails for any component.
Uninstall the benchmarks or test templates.
Args:
test_templates (Iterable[TestTemplate]): The test templates.
Returns:
InstallStatusResult: Result containing the uninstallation status and error message if any.
"""
self.logger.info("Uninstalling test templates.")
uninstall_results = {}
with ThreadPoolExecutor() as executor:
futures = [executor.submit(test_template.uninstall) for test_template in test_templates]
futures = {executor.submit(test_template.uninstall): test_template for test_template in test_templates}
for future in as_completed(futures):
future.result()
test_template = futures[future]
try:
future.result()
uninstall_results[test_template.name] = "Success"
except Exception as e:
self.logger.error(f"Uninstallation failed for {test_template.name}: {e}")
uninstall_results[test_template.name] = str(e)

all_success = all(result == "Success" for result in uninstall_results.values())
if all_success:
return InstallStatusResult(True, "All test templates uninstalled successfully.")
else:
return InstallStatusResult(False, "Some test templates failed to uninstall.", uninstall_results)
42 changes: 20 additions & 22 deletions src/cloudai/installer/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from typing import Iterable

from cloudai._core.base_installer import BaseInstaller
from cloudai._core.install_status_result import InstallStatusResult
from cloudai._core.registry import Registry
from cloudai._core.system import System
from cloudai._core.test_template import TestTemplate
Expand All @@ -25,15 +26,11 @@ class Installer:
"""
A wrapper class that creates and manages a specific installer instance based on the system's configuration.
This class facilitates the initialization of the appropriate installer
based on the system.
This class facilitates the initialization of the appropriate installer based on the system.
Attributes
_installers (dict): A class-level dictionary mapping scheduler
types to their corresponding installer subclasses. Used for
dynamic installer creation based on system configuration.
installer (BaseInstaller): The specific installer instance for the system.
logger (logging.Logger): Logger for capturing installation activities.
installer (BaseInstaller): The specific installer instance for the system.
"""

def __init__(self, system: System):
Expand All @@ -56,12 +53,10 @@ def create_installer(cls, system: System) -> BaseInstaller:
system (System): The system schema object.
Returns:
BaseInstaller: An instance of a subclass of BaseInstaller
suitable for the given system's scheduler.
BaseInstaller: An instance of a subclass of BaseInstaller suitable for the given system's scheduler.
Raises:
NotImplementedError: If no installer is available for the
system's scheduler.
NotImplementedError: If no installer is available for the system's scheduler.
"""
scheduler_type = system.scheduler
registry = Registry()
Expand All @@ -70,38 +65,41 @@ def create_installer(cls, system: System) -> BaseInstaller:
raise NotImplementedError(f"No installer available for scheduler: {scheduler_type}")
return installer_class(system)

def is_installed(self, test_templates: Iterable[TestTemplate]) -> bool:
def is_installed(self, test_templates: Iterable[TestTemplate]) -> InstallStatusResult:
"""
Check if the necessary components for the provided test templates are already installed.
Args:
test_templates (Iterable[TestTemplate]): The list of test templates to
check for installation.
test_templates (Iterable[TestTemplate]): The list of test templates to check for installation.
Returns:
bool: True if all necessary components are installed, False otherwise.
InstallStatusResult: Result containing the installation status and error message if not installed.
"""
self.logger.info("Checking installation status of components.")
return self.installer.is_installed(test_templates)

def install(self, test_templates: Iterable[TestTemplate]):
def install(self, test_templates: Iterable[TestTemplate]) -> InstallStatusResult:
"""
Check if the necessary components are installed and install them if not using the instantiated installer.
Args:
test_templates (Iterable[TestTemplate]): The list of test templates to
install.
test_templates (Iterable[TestTemplate]): The list of test templates to install.
Returns:
InstallStatusResult: Result containing the installation status and error message if not installed.
"""
self.logger.info("Installing test templates.")
self.installer.install(test_templates)
return self.installer.install(test_templates)

def uninstall(self, test_templates: Iterable[TestTemplate]):
def uninstall(self, test_templates: Iterable[TestTemplate]) -> InstallStatusResult:
"""
Uninstall the benchmarks or test templates using the instantiated installer.
Args:
test_templates (Iterable[TestTemplate]): The list of test templates to
uninstall.
test_templates (Iterable[TestTemplate]): The list of test templates to uninstall.
Returns:
InstallStatusResult: Result containing the installation status and error message if not installed.
"""
self.logger.info("Uninstalling test templates.")
self.installer.uninstall(test_templates)
return self.installer.uninstall(test_templates)
Loading

0 comments on commit 44cd218

Please sign in to comment.