Skip to content

Commit

Permalink
Used new group in test_write_socket_known_gid
Browse files Browse the repository at this point in the history
  • Loading branch information
ckunki committed Mar 22, 2024
1 parent 5072c85 commit 5b88756
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 88 deletions.
23 changes: 15 additions & 8 deletions test/docker/dss_docker_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@


def pytest_addoption(parser):
def add_options_for_docker_image(base_name: str, test_group: str = None):
"Notebook testing"
if test_group is None:
test_group = f"tests with {base_name}"
parser.addoption(
f"--docker-image-{base_name}", default=None,
help="Name and version of existing Docker image to use for {test_group}",
)
parser.addoption(
f"--keep-docker-image-{base_name}", action="store_true", default=False,
help=f"Keep the created Docker image for {test_group} for subsequent inspection or reuse.",
)

parser.addoption(
"--dss-docker-image", default=None,
help="Name and version of existing Docker image to use for tests",
Expand All @@ -19,14 +32,8 @@ def pytest_addoption(parser):
"--keep-dss-docker-image", action="store_true", default=False,
help="Keep the created dss docker image for inspection or reuse."
)
parser.addoption(
"--docker-image-notebook-test", default=None,
help="Name and version of existing Docker image for Notebook testing to use for tests",
)
parser.addoption(
"--keep-docker-image-notebook-test", action="store_true", default=False,
help="Keep the created notebook-test docker image for inspection or reuse.",
)
add_options_for_docker_image("notebook-test", "Notebook testing")
add_options_for_docker_image("ai-lab-with-additional-group")
parser.addoption(
"--docker-registry", default=None, metavar="HOST:PORT",
help="Docker registry for pushing Docker images to",
Expand Down
211 changes: 131 additions & 80 deletions test/integration/test_create_dss_docker_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
from tenacity.stop import stop_after_delay
from typing import Set, Tuple
from datetime import datetime, timedelta
from inspect import cleandoc

from exasol.ds.sandbox.lib.logging import set_log_level
from exasol.ds.sandbox.lib import pretty_print
from test.docker.in_memory_build_context import InMemoryBuildContext
from test.docker.image import (
DockerImageSpec,
image,
pull as pull_docker_image,
)
from test.docker.container import (
Expand Down Expand Up @@ -94,6 +97,87 @@ def retry(exception: typing.Type[BaseException], timeout: timedelta):
)


def assert_exec_run(container: Container, command: str, **kwargs) -> str:
"""
Execute command in container and verify success.
"""
exit_code, output = container.exec_run(command, **kwargs)
output = output.decode("utf-8").strip()
assert exit_code == 0, output
return output


class SocketInspector:
def __init__(self, context_provider, socket_on_host: Path):
self._context_provider = context_provider
self.socket_on_host = socket_on_host
self._container = None
self._context = None

def __enter__(self):
self._context = self._context_provider(self.socket_on_host)
self._container = self._context.__enter__()
wait_for_socket_access(self._container)
return self

def __exit__(self, exc_type, exc, exc_tb):
self._container = None
self._context.__exit__(exc_type, exc, exc_tb)

def run(self, command: str, **kwargs) -> str:
return assert_exec_run(self._container, command, **kwargs)

def numeric_gid(self, group_entry: str) -> int:
"""group_entry is "ubuntu:x:971:", for example"""
return int(group_entry.split(':')[2])

def get_gid(self, group_name: str) -> int:
output = self.run(f"getent group {group_name}")
return self.numeric_gid(output)

def get_unassigned_gid(self) -> int:
"""
Return a new gid, that is not used for any other group, yet.
"""
gid = 0
for line in self.run("cat /etc/group").splitlines():
if not line.startswith(f"nogroup:"):
gid = max(gid, self.numeric_gid(line))
return gid + 1

def assert_jupyter_member_of(self, group_name: str):
output = self.run(f"getent group {group_name}")
members = output.split(":")[3].split(",")
assert "jupyter" in members

def assert_write_to_socket(self):
signal = f"Is there anybody out there {datetime.now()}?"
self.run(
f'bash -c "echo {signal} > {DOCKER_SOCKET_CONTAINER}"',
user="jupyter")
assert signal == self.socket_on_host.read_text().strip()


@pytest.fixture
def socket_inspector(dss_container_context, accessible_file):
yield SocketInspector(dss_container_context, accessible_file)


class GroupChanger:
def __init__(self, context_provider):
self._context_provider = context_provider

def chgrp(self, gid: int, path_on_host: Path):
path_in_container = "/mounted"
with self._context_provider(path_on_host, path_in_container) as container:
assert_exec_run(container, f"chgrp {gid} {path_in_container}")


@pytest.fixture
def group_changer(ubuntu_container_context):
return GroupChanger(ubuntu_container_context)


def test_jupyterlab(dss_docker_container, jupyter_port):
""""
Test that jupyterlab is configured properly
Expand Down Expand Up @@ -179,94 +263,61 @@ def test_docker_socket_on_host_touched(dss_container_context, fake_docker_socket
assert stat_before == socket.stat()


def assert_exec_run(container: Container, command: str, **kwargs) -> str:
"""
Execute command in container and verify success.
"""
exit_code, output = container.exec_run(command, **kwargs)
output = output.decode("utf-8").strip()
assert exit_code == 0, output
return output
# def test_write_socket_known_gid(socket_inspector, group_changer):
# with socket_inspector as inspector:
# gid = inspector.get_gid("ubuntu")
# group_changer.chgrp(gid, socket_inspector.socket_on_host)
# with socket_inspector as inspector:
# inspector.assert_jupyter_member_of("ubuntu")
# inspector.assert_write_to_socket()


class SocketInspector:
def __init__(self, context_provider, socket_on_host: Path):
self._context_provider = context_provider
self.socket_on_host = socket_on_host
self._container = None
self._context = None

def __enter__(self):
self._context = self._context_provider(self.socket_on_host)
self._container = self._context.__enter__()
wait_for_socket_access(self._container)
return self

def __exit__(self, exc_type, exc, exc_tb):
self._container = None
self._context.__exit__(exc_type, exc, exc_tb)

def run(self, command: str, **kwargs) -> str:
return assert_exec_run(self._container, command, **kwargs)

def numeric_gid(self, group_entry: str) -> int:
"""group_entry is "ubuntu:x:971:", for example"""
return int(group_entry.split(':')[2])

def get_gid(self, group_name: str) -> int:
output = self.run(f"getent group {group_name}")
return self.numeric_gid(output)

def get_unassigned_gid(self) -> int:
"""
Return a new gid, that is not used for any other group, yet.
@contextmanager
def added_group(request, base_image, gid, group_name):
# dss_docker_image.image_name
dockerfile_content = cleandoc(
f"""
FROM {base_image}
RUN sudo groupadd --gid {gid} {group_name}
"""
gid = 0
for line in self.run("cat /etc/group").splitlines():
if not line.startswith(f"nogroup:"):
gid = max(gid, self.numeric_gid(line))
return gid + 1

def assert_jupyter_member_of(self, group_name: str):
output = self.run(f"getent group {group_name}")
members = output.split(":")[3].split(",")
assert "jupyter" in members

def assert_write_to_socket(self):
signal = f"Is there anybody out there {datetime.now()}?"
self.run(
f'bash -c "echo {signal} > {DOCKER_SOCKET_CONTAINER}"',
user="jupyter")
assert signal == self.socket_on_host.read_text().strip()


@pytest.fixture
def socket_inspector(dss_container_context, accessible_file):
yield SocketInspector(dss_container_context, accessible_file)


class GroupChanger:
def __init__(self, context_provider):
self._context_provider = context_provider
)
with InMemoryBuildContext() as context:
context.add_string_to_file(name="Dockerfile", string=dockerfile_content)
yield from image(request,
name=f"ai_lab_with_additional_group",
fileobj=context.fileobj,
custom_context=True,
print_log=True)

def chgrp(self, gid: int, path_on_host: Path):
path_in_container = "/mounted"
with self._context_provider(path_on_host, path_in_container) as container:
assert_exec_run(container, f"chgrp {gid} {path_in_container}")

def altered_inspector(request, image_name: str, socket_on_host: Path):
def context_provider(socket_on_host):
return container_context(
request,
image_name=image_name,
volumes={ socket_on_host: {
'bind': DOCKER_SOCKET_CONTAINER,
'mode': 'rw', }, },
)
return SocketInspector(context_provider, socket_on_host)

@pytest.fixture
def group_changer(ubuntu_container_context):
return GroupChanger(ubuntu_container_context)

def test_write_socket_known_gid(request, dss_docker_image, socket_inspector, group_changer):
initial_inspector = socket_inspector
with initial_inspector as inspector:
gid = inspector.get_unassigned_gid()

def test_write_socket_known_gid(socket_inspector, group_changer):
with socket_inspector as inspector:
gid = inspector.get_gid("ubuntu")
group_changer.chgrp(gid, socket_inspector.socket_on_host)
with socket_inspector as inspector:
inspector.assert_jupyter_member_of("ubuntu")
inspector.assert_write_to_socket()
socket_on_host = initial_inspector.socket_on_host
group_changer.chgrp(gid, socket_on_host)

# create new image based on dss_docker_image but with an additional
# group with gid set to the one inquired before
base_image = dss_docker_image.image_name
group_name = "artifical_group"
with added_group(request, base_image, gid, group_name) as image:
with altered_inspector(request, image.id, socket_on_host) as inspector:
inspector.assert_jupyter_member_of(group_name)
inspector.assert_write_to_socket()


def test_write_socket_unknown_gid(socket_inspector, group_changer):
Expand Down

0 comments on commit 5b88756

Please sign in to comment.