Skip to content

Commit

Permalink
refactoring ITDE manager interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ahsimb committed Mar 27, 2024
1 parent ad39d29 commit 6ed9e20
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 49 deletions.
72 changes: 41 additions & 31 deletions exasol/nb_connector/itde_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Tuple, Optional
from enum import IntFlag

import docker # type: ignore
from docker.models.networks import Network # type: ignore
Expand All @@ -25,6 +26,14 @@
NAME_SERVER_ADDRESS = "8.8.8.8"


class ItdeContainerStatus(IntFlag):
ABSENT = 0
STOPPED = 1
RUNNING = 3
VISIBLE = 5
READY = RUNNING | VISIBLE


def bring_itde_up(conf: Secrets) -> None:
"""
Launches the ITDE environment using its API. Sets hardcoded environment name,
Expand Down Expand Up @@ -100,20 +109,21 @@ def _add_current_container_to_db_network(network_name: str) -> None:
network.connect(container.id)


def _is_current_container_not_in_db_network(network_name: str) -> bool:
def _is_current_container_visible(network_name: str) -> bool:
"""
For the Docker Edition returns True if the current (AI-Lab) container
is NOT connected to the network with the specified name. Otherwise,
including the cases of other editions, returns False.
is connected to the network with the specified name, otherwise False.
For other editions it always returns True.
"""
with ContextDockerClient() as docker_client:
container = _get_current_container(docker_client)
if not container:
return False
# Not the Docker Edition
return True
network = _get_docker_network(docker_client, network_name)
if not network:
return True
return container not in network.containers
return False
return container in network.containers


def _get_docker_network(docker_client: docker.DockerClient, network_name: str) -> Optional[Network]:
Expand Down Expand Up @@ -144,61 +154,61 @@ def _get_ipv4_addresses():
return ip_addresses


def is_itde_running(conf: Secrets) -> Tuple[bool, bool]:
def get_itde_status(conf: Secrets) -> ItdeContainerStatus:
"""
Checks if the ITDE container exists and ready to be used. In the Docker Edition that
means the ITDE is running and the AI-Lab container is connected to its network. In
other editions it will just check that the ITDE is running.
Returns two boolean flags - (exists, running).
Returns the container status.
The name of the container is taken from the provided secret store.
If the name cannot be found in the secret store the function returns False, False.
If the name cannot be found there the function returns the status ABSENT.
"""

# Try to get the names of the Docker-DB container and its network from the secret store.
container_name = conf.get(AILabConfig.itde_container)
network_name = conf.get(AILabConfig.itde_network)
if not container_name or not network_name:
return False, False
return ItdeContainerStatus.ABSENT

# Check the existence and the status of the container using the Docker API.
with ContextDockerClient() as docker_client:
if docker_client.containers.list(all=True, filters={"name": container_name}):
container = docker_client.containers.get(container_name)
is_ready = (container.status == 'running' and not
_is_current_container_not_in_db_network(network_name))
return True, is_ready
return False, False
if container.status != 'running':
return ItdeContainerStatus.STOPPED
status = ItdeContainerStatus.RUNNING
if _is_current_container_visible(network_name):
status |= ItdeContainerStatus.VISIBLE
return status
return ItdeContainerStatus.ABSENT


def start_itde(conf: Secrets) -> None:
def restart_itde(conf: Secrets) -> None:
"""
Starts an existing ITDE container if it's not already running. In the Docker Edition
connects the AI-Lab container to the Docker-DB network, unless it's already connected
to it.
For this function to work the container must exist. If it doesn't
the docker.errors.NotFound exception will be raised. Use the is_itde_running
function to check if the container exists.
The name of the container is taken from the provided secret store, where it must
exist. Otherwise, a RuntimeError will be raised.
For this function to work the container must exist. If it doesn't a RuntimeError will
be raised. Use the get_itde_status function to check if the container exists.
"""

# The names of the Docker-DB container and its network should be in the secret store.
container_name = conf.get(AILabConfig.itde_container)
network_name = conf.get(AILabConfig.itde_network)
if not container_name or not network_name:
raise RuntimeError('Unable to find the name of the Docker container or its network.')
status = get_itde_status(conf)

# Start the container using the Docker API, unless it's already running.
with ContextDockerClient() as docker_client:
container = docker_client.containers.get(container_name)
if container.status != 'running':
if status == ItdeContainerStatus.ABSENT:
raise RuntimeError("The Docker-DB container doesn't exist.")

if not (status & ItdeContainerStatus.RUNNING):
container_name = conf.get(AILabConfig.itde_container)
with ContextDockerClient() as docker_client:
container = docker_client.containers.get(container_name)
container.start()

_add_current_container_to_db_network(network_name)
if not (status & ItdeContainerStatus.VISIBLE):
network_name = conf.get(AILabConfig.itde_network)
_add_current_container_to_db_network(network_name)


def take_itde_down(conf: Secrets) -> None:
Expand Down
32 changes: 14 additions & 18 deletions test/integration/test_itde_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
from exasol.nb_connector.secret_store import Secrets
from exasol.nb_connector.itde_manager import (
bring_itde_up,
is_itde_running,
start_itde,
get_itde_status,
restart_itde,
take_itde_down,
ItdeContainerStatus,
)

DB_NETWORK_NAME = "db_network_DemoDb"
Expand Down Expand Up @@ -70,18 +71,16 @@ def test_itde_exists_and_running(secrets):

try:
bring_itde_up(secrets)
itde_exists, itde_running = is_itde_running(secrets)
assert itde_exists
assert itde_running
itde_status = get_itde_status(secrets)
assert itde_status & ItdeContainerStatus.RUNNING
finally:
remove_itde()


def test_itde_neither_exists_nor_running(secrets):
remove_itde()
itde_exists, itde_running = is_itde_running(secrets)
assert not itde_exists
assert not itde_running
itde_status = get_itde_status(secrets)
assert itde_status == ItdeContainerStatus.ABSENT


def test_itde_exists_not_running(secrets):
Expand All @@ -91,9 +90,8 @@ def test_itde_exists_not_running(secrets):
try:
bring_itde_up(secrets)
stop_itde(secrets)
itde_exists, itde_running = is_itde_running(secrets)
assert itde_exists
assert not itde_running
itde_status = get_itde_status(secrets)
assert itde_status == ItdeContainerStatus.STOPPED
finally:
remove_itde()

Expand All @@ -105,10 +103,9 @@ def test_itde_start(secrets):
try:
bring_itde_up(secrets)
stop_itde(secrets)
start_itde(secrets)
itde_exists, itde_running = is_itde_running(secrets)
assert itde_exists
assert itde_running
restart_itde(secrets)
itde_status = get_itde_status(secrets)
assert itde_status & ItdeContainerStatus.RUNNING
finally:
remove_itde()

Expand Down Expand Up @@ -145,8 +142,7 @@ def test_take_itde_down_is_not_itde_running(secrets):
try:
bring_itde_up(secrets)
take_itde_down(secrets)
itde_exists, itde_running = is_itde_running(secrets)
assert not itde_exists
assert not itde_running
itde_status = get_itde_status(secrets)
assert itde_status & ItdeContainerStatus.RUNNING
finally:
remove_itde()

0 comments on commit 6ed9e20

Please sign in to comment.