Skip to content

Commit

Permalink
It was possible to install Dev Envs with unavailable images. Now the DEM
Browse files Browse the repository at this point in the history
shows an error and aborts the installation.
  • Loading branch information
janosmurai committed Mar 4, 2024
1 parent 672f648 commit 7f78602
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 92 deletions.
2 changes: 1 addition & 1 deletion dem/cli/command/install_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ def execute(platform: Platform, dev_env_name: str) -> None:
try:
platform.install_dev_env(dev_env_to_install)
except PlatformError as e:
stderr.print(f"[red]Error: {e}[/]")
stderr.print(f"[red]{e}[/]")
else:
stdout.print(f"[green]Successfully installed the {dev_env_name}![/]")
14 changes: 0 additions & 14 deletions dem/core/dev_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,6 @@ def check_image_availability(self, all_tool_images: ToolImages,

return image_statuses

def get_registry_only_tool_images(self, all_tool_images: ToolImages,
update_tool_image_store: bool) -> set:
""" Get the list of registry only tool images.
Returns with the list of registry only tool images.
"""
registry_only_tool_images: set = set()
self.check_image_availability(all_tool_images, update_tool_image_store)
for tool in self.tools:
if tool["image_status"] == ToolImages.REGISTRY_ONLY:
registry_only_tool_images.add(tool["image_name"] + ':' + tool["image_version"])

return registry_only_tool_images

def get_deserialized(self, omit_is_installed: bool = False) -> dict[str, str]:
""" Create the deserialized json.
Expand Down
21 changes: 15 additions & 6 deletions dem/core/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,21 @@ def install_dev_env(self, dev_env_to_install: DevEnv) -> None:
Args:
dev_env_to_install -- the Development Environment to install
"""
for tool_image in dev_env_to_install.get_registry_only_tool_images(self.tool_images, False):
self.user_output.msg(f"\nPulling image {tool_image}", is_title=True)
try:
self.container_engine.pull(tool_image)
except ContainerEngineError:
raise PlatformError("Dev Env install failed.")
dev_env_to_install.check_image_availability(self.tool_images, False)

# First check if the missing images are available in the registries.
for tool in dev_env_to_install.tools:
if tool["image_status"] == ToolImages.NOT_AVAILABLE:
raise PlatformError(f"The {tool['image_name']}:{tool['image_version']} image is not available.")

for tool in dev_env_to_install.tools:
if tool["image_status"] == ToolImages.REGISTRY_ONLY:
self.user_output.msg(f"\nPulling image {tool['image_name']}:{tool['image_version']}",
is_title=True)
try:
self.container_engine.pull(f"{tool['image_name']}:{tool['image_version']}")
except ContainerEngineError as e:
raise PlatformError(f"Dev Env install failed. Reason: {str(e)}")

dev_env_to_install.is_installed = True
self.flush_descriptors()
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/test_install_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,5 @@ def test_install_dev_env_valid_name_failed(mock_stderr_print):
assert 0 == runner_result.exit_code

mock_platform.get_dev_env_by_name.assert_called_once_with(fake_dev_env_to_install.name )
mock_stderr_print.assert_called_once_with(f"[red]Error: Platform error: {test_exception_text}[/]")
mock_stderr_print.assert_called_once_with(f"[red]Platform error: {test_exception_text}[/]")

45 changes: 0 additions & 45 deletions tests/core/test_dev_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,53 +196,8 @@ def test_DevEnv_check_image_availability_local_only():
for idx, tool in enumerate(test_dev_env.tools):
assert expected_statuses[idx] == tool["image_status"]


mock_tool_images.local.update.assert_called_once()

def test_DevEnv_get_registry_only_tool_images() -> None:
# Test setup
test_descriptor = {
"name": "test_name",
"installed": "False",
"tools": [
{
"image_name": "test_image_name1",
"image_version": "test_image_tag1"
},
{
"image_name": "test_image_name2",
"image_version": "test_image_tag2"
},
{
"image_name": "test_image_name3",
"image_version": "test_image_tag3"
},
{
"image_name": "test_image_name4",
"image_version": "test_image_tag4"
},
]
}
mock_tool_images = MagicMock()
mock_tool_images.local.elements = [
"test_image_name1:test_image_tag1",
"test_image_name2:test_image_tag2"
]
mock_tool_images.registry.elements = [
"test_image_name1:test_image_tag1",
"test_image_name3:test_image_tag3"
]
test_dev_env = dev_env.DevEnv(test_descriptor)

# Run unit under test
actual_registry_onnly_tool_images = test_dev_env.get_registry_only_tool_images(mock_tool_images, True)

# Check expectations
expected_registry_only_tool_images = {
"test_image_name3:test_image_tag3",
}
assert expected_registry_only_tool_images == actual_registry_onnly_tool_images

def test_DevEnv_get_deserialized_is_installed_true() -> None:
# Test setup
test_descriptor: dict[str, Any] = {
Expand Down
113 changes: 88 additions & 25 deletions tests/core/test_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,63 +272,126 @@ def test_Platform_get_dev_env_by_name_no_match(mock___init__: MagicMock) -> None
@patch.object(platform.Platform, "user_output")
@patch.object(platform.Platform, "__init__")
def test_Platform_install_dev_env_succes(mock___init__: MagicMock, mock_user_input: MagicMock,
mock_container_engine: MagicMock, mock_tool_images,
mock_flush_descriptors: MagicMock) -> None:
mock_container_engine: MagicMock, mock_tool_images,
mock_flush_descriptors: MagicMock) -> None:
# Test setup
mock___init__.return_value = None


test_dev_env = MagicMock()
test_registry_only_tool_images: list[str] = ["test_image_name1:test_image_version1",
"test_image_name2:test_image_version2"]
test_dev_env.get_registry_only_tool_images.return_value = test_registry_only_tool_images
mock_dev_env = MagicMock()
mock_dev_env.tools = [
{
"image_name": "test_image_name0",
"image_version": "test_image_version0",
"image_status": platform.ToolImages.LOCAL_AND_REGISTRY
},
{
"image_name": "test_image_name1",
"image_version": "test_image_version1",
"image_status": platform.ToolImages.REGISTRY_ONLY
},
{
"image_name": "test_image_name2",
"image_version": "test_image_version2",
"image_status": platform.ToolImages.REGISTRY_ONLY
},
{
"image_name": "test_image_name3",
"image_version": "test_image_version3",
"image_status": platform.ToolImages.LOCAL_ONLY
}
]

test_platform = platform.Platform()

# Run unit under test
test_platform.install_dev_env(test_dev_env)
test_platform.install_dev_env(mock_dev_env)

# Check expectations
mock___init__.assert_called_once()

test_dev_env.get_registry_only_tool_images.assert_called_once_with(mock_tool_images, False)
mock_dev_env.check_image_availability.assert_called_once_with(mock_tool_images, False)
expected_registry_only_tool_images: list[str] = ["test_image_name1:test_image_version1",
"test_image_name2:test_image_version2"]
mock_user_input.msg.assert_has_calls([
call(f"\nPulling image {test_registry_only_tool_images[0]}", is_title=True),
call(f"\nPulling image {test_registry_only_tool_images[1]}", is_title=True)
call(f"\nPulling image {expected_registry_only_tool_images[0]}", is_title=True),
call(f"\nPulling image {expected_registry_only_tool_images[1]}", is_title=True)
])
mock_container_engine.pull.assert_has_calls([
call(test_registry_only_tool_images[0]),
call(test_registry_only_tool_images[1])
call(expected_registry_only_tool_images[0]),
call(expected_registry_only_tool_images[1])
])
mock_flush_descriptors.assert_called_once()

@patch.object(platform.Platform, "tool_images")
@patch.object(platform.Platform, "container_engine")
@patch.object(platform.Platform, "user_output")
@patch.object(platform.Platform, "__init__")
def test_Platform_install_dev_env_failure(mock___init__: MagicMock, mock_user_output: MagicMock,
mock_container_engine: MagicMock,mock_tool_images) -> None:
def test_Platform_install_dev_env_pull_failure(mock___init__: MagicMock, mock_user_output: MagicMock,
mock_container_engine: MagicMock,mock_tool_images) -> None:
# Test setup
mock___init__.return_value = None


test_dev_env = MagicMock()
test_registry_only_tool_images: list[str] = ["test_image_name1:test_image_version1",
"test_image_name2:test_image_version2"]
test_dev_env.get_registry_only_tool_images.return_value = test_registry_only_tool_images
mock_dev_env = MagicMock()
mock_dev_env.tools = [
{
"image_name": "test_image_name1",
"image_version": "test_image_version1",
"image_status": platform.ToolImages.REGISTRY_ONLY
}
]

test_exception_text = "test_exception_text"
mock_container_engine.pull.side_effect = platform.ContainerEngineError(test_exception_text)

test_platform = platform.Platform()

mock_container_engine.pull.side_effect = platform.ContainerEngineError("")

# Run unit under test
with pytest.raises(platform.PlatformError) as exported_exception_info:
test_platform.install_dev_env(test_dev_env)
test_platform.install_dev_env(mock_dev_env)

# Check expectations
mock___init__.assert_called_once()
# Check expectations
assert str(exported_exception_info.value) == f"Platform error: Dev Env install failed. Reason:" + \
f" Container engine error: {test_exception_text}"

mock___init__.assert_called_once()

mock_dev_env.check_image_availability.assert_called_once_with(mock_tool_images, False)
expected_registry_only_tool_image = "test_image_name1:test_image_version1"
mock_user_output.msg.assert_called_once_with(f"\nPulling image {expected_registry_only_tool_image}",
is_title=True)
mock_container_engine.pull.assert_called_once_with(expected_registry_only_tool_image)

@patch.object(platform.Platform, "tool_images")
@patch.object(platform.Platform, "container_engine")
@patch.object(platform.Platform, "user_output")
@patch.object(platform.Platform, "__init__")
def test_Platform_install_dev_env_not_avilable(mock___init__: MagicMock, mock_user_output: MagicMock,
mock_container_engine: MagicMock,mock_tool_images) -> None:
# Test setup
mock___init__.return_value = None

mock_dev_env = MagicMock()
mock_dev_env.tools = [
{
"image_name": "test_image_name1",
"image_version": "test_image_version1",
"image_status": platform.ToolImages.NOT_AVAILABLE
}
]

test_platform = platform.Platform()

# Run unit under test
with pytest.raises(platform.PlatformError) as exported_exception_info:
test_platform.install_dev_env(mock_dev_env)

# Check expectations
assert str(exported_exception_info.value) == f"Platform error: The test_image_name1:test_image_version1 image is not available."

mock___init__.assert_called_once()

assert str(exported_exception_info) == "Platform error: Dev Env install failed."
mock_dev_env.check_image_availability.assert_called_once_with(mock_tool_images, False)

@patch.object(platform.Platform, "flush_descriptors")
@patch.object(platform.Platform, "container_engine")
Expand Down

0 comments on commit 7f78602

Please sign in to comment.