5
5
import logging
6
6
import os
7
7
import platform
8
+ import time
8
9
import urllib .error
9
10
import urllib .request
10
11
18
19
import testcontainers .core .waiting_utils
19
20
20
21
import pytest
22
+ import pytest_subtests
21
23
22
24
from tests .containers import docker_utils , podman_machine_utils
23
25
@@ -31,7 +33,7 @@ class TestWorkbenchImage:
31
33
# disable ipv6 https://danwalsh.livejournal.com/47118.html
32
34
{"net.ipv6.conf.all.disable_ipv6" : "1" }
33
35
])
34
- def test_image_entrypoint_starts (self , image : str , sysctls ) -> None :
36
+ def test_image_entrypoint_starts (self , subtests : pytest_subtests . SubTests , image : str , sysctls ) -> None :
35
37
skip_if_not_workbench_image (image )
36
38
37
39
container = WorkbenchContainer (image = image , user = 1000 , group_add = [0 ],
@@ -40,17 +42,16 @@ def test_image_entrypoint_starts(self, image: str, sysctls) -> None:
40
42
try :
41
43
container .start ()
42
44
# check explicitly that we can connect to the ide running in the workbench
43
- container ._connect ()
45
+ with subtests .test ("Attempting to connect to the workbench..." ):
46
+ container ._connect ()
44
47
finally :
45
48
# try to grab logs regardless of whether container started or not
46
- stdout , stderr = container .get_logs ()
47
- for line in stdout .splitlines () + stderr .splitlines ():
48
- logging .debug (line )
49
+ grab_and_check_logs (subtests , container )
49
50
finally :
50
51
docker_utils .NotebookContainer (container ).stop (timeout = 0 )
51
52
52
53
@pytest .mark .skip (reason = "RHOAIENG-17305: currently our Workbench images don't tolerate IPv6" )
53
- def test_ipv6_only (self , image : str , test_frame ):
54
+ def test_ipv6_only (self , subtests : pytest_subtests . SubTests , image : str , test_frame ):
54
55
"""Test that workbench image is accessible via IPv6.
55
56
Workarounds for macOS will be needed, so that's why it's a separate test."""
56
57
skip_if_not_workbench_image (image )
@@ -103,9 +104,7 @@ def test_ipv6_only(self, image: str, test_frame):
103
104
container ._connect (container_host = host , container_port = port )
104
105
finally :
105
106
# try to grab logs regardless of whether container started or not
106
- stdout , stderr = container .get_logs ()
107
- for line in stdout .splitlines () + stderr .splitlines ():
108
- logging .debug (line )
107
+ grab_and_check_logs (subtests , container )
109
108
finally :
110
109
docker_utils .NotebookContainer (container ).stop (timeout = 0 )
111
110
@@ -192,3 +191,39 @@ def skip_if_not_workbench_image(image: str) -> docker.models.images.Image:
192
191
f"Image { image } does not have any of '{ ide_server_label_fragments = } in { image_metadata .labels ['name' ]= } '" )
193
192
194
193
return image_metadata
194
+
195
+ def grab_and_check_logs (subtests : pytest_subtests .SubTests , container : WorkbenchContainer ) -> None :
196
+ # Here is a list of blocked keywords we don't want to see in the log messages during the container/workbench
197
+ # startup (e.g. log messages from Jupyter IDE, code-server IDE or RStudio IDE).
198
+ blocked_keywords = ["Error" , "error" , "Warning" , "warning" , "Failed" , "failed" , "[W " , "[E " , "Traceback" ]
199
+ # Here is a list of allowed messages that match some block keyword from the list above, but we allow them
200
+ # for some reason.
201
+ allowed_messages = [
202
+ # We don't touch this - it should be fixed in the future by the package upgrade.
203
+ "JupyterEventsVersionWarning: The `version` property of an event schema must be a string. It has been type coerced, but in a future version of this library, it will fail to validate." ,
204
+ # This is a message from our reverse proxy nginx caused by the fact that we attempt to connect to the code-server before it's actually running (container.start(wait_for_readiness=True)).
205
+ "connect() failed (111: Connection refused) while connecting to upstream, client" ,
206
+ ]
207
+
208
+ # Let's wait couple of seconds to give a chance to log eventual extra startup messages just to be sure we don't
209
+ # miss anytihng important in this test.
210
+ time .sleep (3 )
211
+
212
+ stdout , stderr = container .get_logs ()
213
+ full_logs = ""
214
+ for line in stdout .splitlines () + stderr .splitlines ():
215
+ full_logs = "\n " .join ([full_logs , line .decode ()])
216
+ logging .debug (line .decode ())
217
+
218
+ # let's check that logs don't contain any error or unexpected warning message
219
+ with subtests .test ("Checking the log in the workbench..." ):
220
+ failed_lines : list [str ] = []
221
+ for line in full_logs .splitlines ():
222
+ if any (keyword in line for keyword in blocked_keywords ):
223
+ if any (allowed in line for allowed in allowed_messages ):
224
+ logging .debug (f"Waived message: '{ line } '" )
225
+ else :
226
+ logging .error (f"Unexpected keyword in the following message: '{ line } '" )
227
+ failed_lines .append (line )
228
+ if len (failed_lines ) > 0 :
229
+ pytest .fail (f"Log message(s) ({ len (failed_lines )} ) that violate our checks occurred during the workbench startup:\n { "\n " .join (failed_lines )} " )
0 commit comments