Skip to content

Commit e6c4978

Browse files
Merge pull request #835 from jiridanek/jd_ipv6_test
RHOAIENG-17305: chore(tests): IPv4 compatibility test(s), checking that we did not break the single-stack IPv4 case
2 parents c5b27af + 9dafdbd commit e6c4978

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import functools
2+
import http.cookiejar
3+
import logging
4+
import urllib.error
5+
import urllib.request
6+
7+
import docker.errors
8+
import docker.models.images
9+
10+
import testcontainers.core.container
11+
import testcontainers.core.waiting_utils
12+
13+
import pytest
14+
15+
from tests.containers import docker_utils
16+
17+
18+
class TestWorkbenchImage:
19+
"""Tests for workbench images in this repository.
20+
A workbench image is an image running a web IDE that listens on port 8888."""
21+
22+
@pytest.mark.parametrize('sysctls', [
23+
{},
24+
# disable ipv6 https://danwalsh.livejournal.com/47118.html
25+
{"net.ipv6.conf.all.disable_ipv6": "1"}
26+
])
27+
def test_image_entrypoint_starts(self, image: str, sysctls) -> None:
28+
skip_if_not_workbench_image(image)
29+
30+
container = WorkbenchContainer(image=image, user=1000, group_add=[0],
31+
sysctls=sysctls,
32+
# because rstudio only prints out errors when TTY is present
33+
# > TTY detected. Printing informational message about logging configuration.
34+
tty=True,
35+
# another rstudio speciality, without this, it gives
36+
# > system error 13 (Permission denied) [path: /opt/app-root/src/.cache/rstudio
37+
# equivalent podman command may include
38+
# > --mount type=tmpfs,dst=/opt/app-root/src,notmpcopyup
39+
# can't use mounts= because testcontainers already sets volumes=
40+
# > mounts=[docker.types.Mount(target="/opt/app-root/src/", source="", type="volume", no_copy=True)],
41+
# can use tmpfs=, keep in mind `notmpcopyup` opt is podman specific
42+
tmpfs={"/opt/app-root/src": "rw,notmpcopyup"},
43+
)
44+
try:
45+
try:
46+
container.start()
47+
# check explicitly that we can connect to the ide running in the workbench
48+
container._connect()
49+
finally:
50+
# try to grab logs regardless of whether container started or not
51+
stdout, stderr = container.get_logs()
52+
for line in stdout.splitlines() + stderr.splitlines():
53+
logging.debug(line)
54+
finally:
55+
docker_utils.NotebookContainer(container).stop(timeout=0)
56+
57+
58+
class WorkbenchContainer(testcontainers.core.container.DockerContainer):
59+
@functools.wraps(testcontainers.core.container.DockerContainer.__init__)
60+
def __init__(
61+
self,
62+
port: int = 8888,
63+
**kwargs,
64+
) -> None:
65+
super().__init__(**kwargs)
66+
67+
self.port = port
68+
self.with_exposed_ports(self.port)
69+
70+
@testcontainers.core.waiting_utils.wait_container_is_ready(urllib.error.URLError)
71+
def _connect(self) -> None:
72+
# are we still alive?
73+
self.get_wrapped_container().reload()
74+
assert self.get_wrapped_container().status != "exited"
75+
76+
# connect
77+
try:
78+
# if we did not enable cookies support here, with RStudio we'd end up looping and getting
79+
# HTTP 302 (i.e. `except urllib.error.HTTPError as e: assert e.code == 302`) every time
80+
cookie_jar = http.cookiejar.CookieJar()
81+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie_jar))
82+
result = opener.open(
83+
urllib.request.Request(f"http://{self.get_container_host_ip()}:{self.get_exposed_port(self.port)}"),
84+
timeout=1)
85+
except urllib.error.URLError as e:
86+
raise e
87+
88+
# get /
89+
try:
90+
if result.status != 200:
91+
raise ConnectionError(f"Failed to connect to container, {result.status=}")
92+
finally:
93+
result.close()
94+
95+
def start(self):
96+
super().start()
97+
container_id = self.get_wrapped_container().id
98+
docker_client = testcontainers.core.container.DockerClient().client
99+
logging.debug(docker_client.api.inspect_container(container_id)['HostConfig'])
100+
self._connect()
101+
return self
102+
103+
104+
def skip_if_not_workbench_image(image: str) -> docker.models.images.Image:
105+
client = testcontainers.core.container.DockerClient()
106+
try:
107+
image_metadata = client.client.images.get(image)
108+
except docker.errors.ImageNotFound:
109+
image_metadata = client.client.images.pull(image)
110+
assert isinstance(image_metadata, docker.models.images.Image)
111+
112+
ide_server_label_fragments = ('-code-server-', '-jupyter-', '-rstudio-')
113+
if not any(ide in image_metadata.labels['name'] for ide in ide_server_label_fragments):
114+
pytest.skip(
115+
f"Image {image} does not have any of '{ide_server_label_fragments=} in {image_metadata.labels['name']=}'")
116+
117+
return image_metadata

0 commit comments

Comments
 (0)