Skip to content

Commit

Permalink
Reduce registry tool image information retrieval to modify, create an…
Browse files Browse the repository at this point in the history
…d list-tools commands.
  • Loading branch information
janosmurai committed Oct 8, 2024
1 parent c3ad86f commit e96d2f6
Show file tree
Hide file tree
Showing 19 changed files with 149 additions and 284 deletions.
1 change: 1 addition & 0 deletions dem/cli/command/create_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def execute(platform: Platform, dev_env_name: str) -> None:
Exceptions:
Abort -- if the name of the Development Environment contains whitespace characters
"""
platform.get_tool_image_info_from_registries = True
platform.assign_tool_image_instances_to_all_dev_envs()

create_dev_env(platform, dev_env_name)
Expand Down
38 changes: 18 additions & 20 deletions dem/cli/command/info_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,12 @@
# dem/cli/command/info_cmd.py

from dem.core.tool_images import ToolImage
from dem.core.dev_env import DevEnv, DevEnv
from dem.cli.console import stdout, stderr
from dem.core.dev_env import DevEnv
from dem.core.platform import Platform
from dem.cli.console import stdout, stderr
from rich.table import Table
import typer

image_status_messages = {
ToolImage.NOT_AVAILABLE: "[red]Error: not available![/]",
ToolImage.LOCAL_ONLY: "Local",
ToolImage.REGISTRY_ONLY: "Registry",
ToolImage.LOCAL_AND_REGISTRY: "Local and Registry",
}

def print_status(platform: Platform, dev_env: DevEnv) -> None:
""" Print the status of the Development Environment.
Expand All @@ -41,13 +34,24 @@ def print_tools_info_table(dev_env: DevEnv, is_local: bool, platform: Platform =
"""
tool_info_table = Table(title="Tools")
tool_info_table.add_column("Image")
tool_info_table.add_column("Availability")

if is_local:
tool_info_table.add_column("Available Locally")

for tool_image in sorted(dev_env.tool_images, key=lambda x: x.name):
tool_info_table.add_row(tool_image.name,
image_status_messages[tool_image.availability])
if is_local:
if tool_image.availability == ToolImage.LOCAL_ONLY:
available_locally_output = "[green]\u2713[/]"
else:
available_locally_output = "[red]\u2717[/]"

tool_info_table.add_row(tool_image.name, available_locally_output)
else:
tool_info_table.add_row(tool_image.name)

if is_local:
print_status(platform, dev_env)

stdout.print(tool_info_table)

def print_tasks_info_table(dev_env: DevEnv) -> None:
Expand Down Expand Up @@ -77,11 +81,8 @@ def print_local_dev_env_info(platform: Platform, dev_env: DevEnv) -> None:
if dev_env.tasks:
print_tasks_info_table(dev_env)

if dev_env.is_installed and dev_env.get_tool_image_status() == DevEnv.Status.REINSTALL_NEEDED:
stderr.print("\n[red]Error: Incomplete local install! The Dev Env must be reinstalled![/]")

if dev_env.get_tool_image_status() == DevEnv.Status.UNAVAILABLE_IMAGE:
stderr.print("\n[red]Error: Required image could not be found either locally or in the registry![/]")
if dev_env.is_installed and not dev_env.is_installation_correct():
stderr.print("\n[red]Error: Incorrect local install![/]")

def local_info(platform: Platform, dev_env_name: str) -> None:
""" Gather and print information about the given local Development Environment.
Expand Down Expand Up @@ -116,9 +117,6 @@ def print_cat_dev_env_info(dev_env: DevEnv, cat_name: str) -> None:
if dev_env.tasks:
print_tasks_info_table(dev_env)

if dev_env.get_tool_image_status() == DevEnv.Status.UNAVAILABLE_IMAGE:
stderr.print("\n[red]Error: Required image could not be found in the registry![/]")

def cat_dev_env_info(platform: Platform, dev_env_name: str, selected_cats: list[str]) -> None:
""" Gather and print information about the given catalog Development Environment.
Expand Down
10 changes: 4 additions & 6 deletions dem/cli/command/list_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ def add_dev_env_info_to_table(platform: Platform, table: Table, dev_env: DevEnv)
if dev_env.is_installed:
dev_env.assign_tool_image_instances(platform.tool_images)
installed_column = "[green]✓[/]"
tool_image_status = dev_env.get_tool_image_status()
if tool_image_status == DevEnv.Status.UNAVAILABLE_IMAGE:
status_column = "[red]Error: Required image is not available![/]"
elif tool_image_status == DevEnv.Status.REINSTALL_NEEDED:
status_column = "[red]Error: Incomplete local install![/]"
else:

if dev_env.is_installation_correct():
status_column = "[green]Ok[/]"
else:
status_column = "[red]Error: Incorrect installation![/]"

if dev_env.name == platform.default_dev_env_name:
default_column = "[green]✓[/]"
Expand Down
10 changes: 2 additions & 8 deletions dem/cli/command/list_tools_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ def list_local_tools(platform: Platform) -> None:
Exceptions:
typer.Abort -- if no local tool images are available
"""
# by settings this to True, the update method won't try to update the registry tools
platform.local_only = True

if not platform.tool_images.all_tool_images:
stdout.print("[yellow]No local tool images are available.[/]")
raise typer.Abort()
Expand All @@ -39,10 +36,6 @@ def update_tools_from_selected_regs(platform: Platform, selected_regs: list[str]
Exceptions:
typer.Abort -- if an unknown registry is specified
"""
# by settings this to True, the update method won't start automatically so we can provide
# the selected registry names
platform.disable_tool_update = True

available_regs = set([reg["name"] for reg in platform.config_file.registries])
selected_regs = set(selected_regs)

Expand All @@ -51,7 +44,7 @@ def update_tools_from_selected_regs(platform: Platform, selected_regs: list[str]
stderr.print(f"[red]Error: Registry {unkown_reg} is not available![/]")
raise typer.Abort()

platform.tool_images.update(reg_selection=selected_regs)
platform.tool_images.update(False, True, reg_selection=selected_regs)

def list_tools_from_regs(platform: Platform, table: Table) -> None:
""" List the available tools from the registries.
Expand Down Expand Up @@ -105,6 +98,7 @@ def list_tools_from_all_regs(platform: Platform) -> None:
Exceptions:
typer.Abort -- if no tool images are available in the registries
"""
platform.tool_images.update(False, True)
if not platform.tool_images.get_registry_ones():
stdout.print("[yellow]No tool images are available in the registries.[/]")
raise typer.Abort()
Expand Down
1 change: 1 addition & 0 deletions dem/cli/command/modify_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def execute(platform: Platform, dev_env_name: str) -> None:
typer.Abort -- if the user cancels the operation
"""

platform.get_tool_image_info_from_registries = True
platform.assign_tool_image_instances_to_all_dev_envs()

dev_env = platform.get_dev_env_by_name(dev_env_name)
Expand Down
25 changes: 5 additions & 20 deletions dem/cli/command/run_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,13 @@ def dev_env_health_check(platform: Platform, dev_env: DevEnv) -> None:
Raises:
typer.Abort -- if the Development Environment has missing tool images
"""
# Don't load the registry images to save time.
platform.local_only = True

dev_env.assign_tool_image_instances(platform.tool_images)
dev_env_tool_status = dev_env.get_tool_image_status()

if dev_env_tool_status == DevEnv.Status.UNAVAILABLE_IMAGE:
stderr.print("[red]Error: Required tool images are not available![/]")
stdout.print("Trying to locate the missing tool images...")
# Now we need to load the registry images.
platform.tool_images.update()
dev_env.assign_tool_image_instances(platform.tool_images)
# Get the tool status again.
dev_env_tool_status = dev_env.get_tool_image_status()
if dev_env_tool_status == DevEnv.Status.REINSTALL_NEEDED:
stdout.print("The missing images are available from the registries.")
typer.confirm("Should DEM reinstall the missing images?", abort=True)
platform.install_dev_env(dev_env)
stdout.print(f"[green]DEM successfully fixed the {dev_env.name} Development Environment![/]")
else:
stderr.print("[red]Error: The missing tool images could not be found in the registries![/]")
raise typer.Abort()
if not dev_env.is_installation_correct():
stderr.print("[red]Error: Incorrect installation![/]")
typer.confirm("Should DEM reinstall the DevEnv?", abort=True)
platform.install_dev_env(dev_env)
stdout.print(f"[green]DEM successfully fixed the {dev_env.name} Development Environment![/]")

def execute(platform: Platform, dev_env_name: str, task_name: str) -> None:
""" Run a task in a Development Environment.
Expand Down
2 changes: 1 addition & 1 deletion dem/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def assign(dev_env_name: Annotated[str, typer.Argument(help="Name of the Dev Env
@typer_cli.command()
def init(project_path: Annotated[str, typer.Argument(help="Path of the project.")] = os.getcwd()) -> None:
"""
Initialize a project to use a Development Environment.
Initialize the DevEnv assigned to the project.
If the project path is not specified, the current working directory will be used.
"""
Expand Down
32 changes: 11 additions & 21 deletions dem/core/dev_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@
class DevEnv():
""" A Development Environment. """

class Status(Enum):
""" The status of an installed Development Environment. """
OK = 0
REINSTALL_NEEDED = 1
UNAVAILABLE_IMAGE = 2

def __init__(self, descriptor: dict | None = None, descriptor_path: str | None = None) -> None:
""" Init the DevEnv class.
Expand Down Expand Up @@ -94,23 +88,19 @@ def del_task(self, task_name: str) -> None:
else:
raise KeyError(f"Task [bold]{task_name}[/] not found.")

def get_tool_image_status(self) -> Status:
""" Get the status of the Tool Images.
This method checks the availability of the assigned Tool Images.
If at least one of the Tool Images is unkonwn: NOT_AVAILABLE.
If at least one of the Tool Images is only available in the registry: REINSTALL_NEEDED.
If all the Tool Images are available: OK.
def is_installation_correct(self) -> bool:
""" Check if the installation is correct.
Returns:
Status -- the status of the Dev Env
Return True if the Dev Env is in installed state and all the Tool Images are available,
otherwise False.
"""
for tool_image in self.tool_images:
if tool_image.availability == ToolImage.NOT_AVAILABLE:
return self.Status.UNAVAILABLE_IMAGE
elif tool_image.availability == ToolImage.REGISTRY_ONLY:
return self.Status.REINSTALL_NEEDED
return self.Status.OK
if self.is_installed:
for tool_image in self.tool_images:
if tool_image.availability == ToolImage.NOT_AVAILABLE:
break
else:
return True
return False

def get_deserialized(self, omit_is_installed: bool = False) -> dict[str, str]:
""" Create the deserialized json.
Expand Down
19 changes: 5 additions & 14 deletions dem/core/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,8 @@ def __init__(self) -> None:
self.local_dev_envs: list[DevEnv] = []
self.are_tool_images_assigned: bool = False

# Set this to true in the platform instance to work with the local tool images only
self.local_only = False
# Set this to true in the platform instance so when first accessing the `tool_images`
# instance variable, do not automatically update the tool images from the registries
self.disable_tool_update = False
# Set this to true in the platform instance to get the tool image info from the registries
self.get_tool_image_info_from_registries = False

def load_dev_envs(self) -> None:
""" Load the Development Environments from the dev_env.json file.
Expand Down Expand Up @@ -75,8 +72,7 @@ def tool_images(self) -> ToolImages:
"""
if self._tool_images is None:
self._tool_images = ToolImages(self.container_engine, self.registries)
if not self.disable_tool_update:
self._tool_images.update(local_only=self.local_only)
self._tool_images.update(True, self.get_tool_image_info_from_registries)
return self._tool_images

@property
Expand Down Expand Up @@ -157,14 +153,9 @@ def install_dev_env(self, dev_env_to_install: DevEnv) -> None:
Args:
dev_env_to_install -- the Development Environment to install
"""
# First check if the missing images are available in the registries, so DEM won't start to
# pull the images and then fail.
for tool_image in dev_env_to_install.tool_images:
if tool_image.availability == ToolImage.NOT_AVAILABLE:
raise PlatformError(f"The {tool_image.name} image is not available.")

for tool_image in dev_env_to_install.tool_images:
if tool_image.availability == ToolImage.REGISTRY_ONLY:
if tool_image.availability is ToolImage.REGISTRY_ONLY or \
tool_image.availability is ToolImage.NOT_AVAILABLE:
self.user_output.msg(f"\nPulling image {tool_image.name}", is_title=True)
try:
self.container_engine.pull(tool_image.name)
Expand Down
13 changes: 6 additions & 7 deletions dem/core/tool_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,22 @@ def __init__(self, container_engine: ContainerEngine, registries: Registries) ->
"""
self.container_engine = container_engine
self.registries = registries
self.all_tool_images = {}
self.all_tool_images: dict[str, ToolImage] = {}

def update(self, local_only: bool = False, registry_only: bool = False,
reg_selection: list[str] = []) -> None:
def update(self, local: bool, registry: bool, reg_selection: list[str] = []) -> None:
""" Update the list of available tools. If the tool image already exists, it will be updated.
Args:
local_only -- update the local tools only
registry_only -- update the registry tools only
local -- get the local tools
registry -- get the registry tools
"""
registry_tool_image_names = []
local_tool_image_names = []

if not registry_only:
if local:
local_tool_image_names = self.container_engine.get_local_tool_images()

if not local_only:
if registry:
registry_tool_image_names = self.registries.list_repos(reg_selection)

for tool_image_name in local_tool_image_names:
Expand Down
20 changes: 3 additions & 17 deletions tests/cli/test_create_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,38 +246,24 @@ def test_execute(mock_create_dev_env: MagicMock, mock_stdout_print: MagicMock) -
mock_platform = MagicMock()
main.platform = mock_platform

mock_dev_env = MagicMock()
mock_platform.get_tool_image_info_from_registries = False
expected_dev_env_name = "test_dev_env"

# Run unit under test
runner_result = runner.invoke(main.typer_cli, ["create", expected_dev_env_name], color=True)

# Check expectations
assert 0 == runner_result.exit_code
assert mock_platform.get_tool_image_info_from_registries is True

mock_platform.assign_tool_image_instances_to_all_dev_envs.assert_called_once()
mock_create_dev_env.assert_called_once_with(mock_platform, expected_dev_env_name)
mock_platform.flush_dev_env_properties.assert_called_once()
mock_stdout_print.assert_has_calls([
call(f"The [green]{expected_dev_env_name}[/] Development Environment has been created!"),
call("Run [italic]dem install[/] to install it.")
])

@patch("dem.cli.command.create_cmd.create_dev_env")
def test_execute_failure(mock_create_dev_env):
# Test setup
mock_platform = MagicMock()
main.platform = mock_platform

expected_dev_env_name = "test_dev_env"

# Run unit under test
runner_result = runner.invoke(main.typer_cli, ["create", expected_dev_env_name], color=True)

# Check expectations
assert 0 == runner_result.exit_code

mock_create_dev_env.assert_called_once_with(mock_platform, expected_dev_env_name)

@patch("dem.cli.command.create_cmd.stderr.print")
def test_create_dev_env_with_whitespace(mock_stderr_print):
# Test setup
Expand Down
Loading

0 comments on commit e96d2f6

Please sign in to comment.