@@ -16,6 +16,12 @@ def test_cli_args(container, http_client):
1616 resp .raise_for_status ()
1717 logs = c .logs (stdout = True ).decode ("utf-8" )
1818 LOGGER .debug (logs )
19+ assert "ERROR" not in logs
20+ warnings = [
21+ warning for warning in logs .split ("\n " ) if warning .startswith ("WARNING" )
22+ ]
23+ assert len (warnings ) == 1
24+ assert warnings [0 ].startswith ("WARNING: Jupyter Notebook deprecation notice" )
1925 assert "login_submit" not in resp .text
2026
2127
@@ -24,7 +30,7 @@ def test_unsigned_ssl(container, http_client):
2430 """Container should generate a self-signed SSL certificate
2531 and notebook server should use it to enable HTTPS.
2632 """
27- container .run (environment = ["GEN_CERT=yes" ])
33+ c = container .run (environment = ["GEN_CERT=yes" ])
2834 # NOTE: The requests.Session backing the http_client fixture does not retry
2935 # properly while the server is booting up. An SSL handshake error seems to
3036 # abort the retry logic. Forcing a long sleep for the moment until I have
@@ -33,6 +39,13 @@ def test_unsigned_ssl(container, http_client):
3339 resp = http_client .get ("https://localhost:8888" , verify = False )
3440 resp .raise_for_status ()
3541 assert "login_submit" in resp .text
42+ logs = c .logs (stdout = True ).decode ("utf-8" )
43+ assert "ERROR" not in logs
44+ warnings = [
45+ warning for warning in logs .split ("\n " ) if warning .startswith ("WARNING" )
46+ ]
47+ assert len (warnings ) == 1
48+ assert warnings [0 ].startswith ("WARNING: Jupyter Notebook deprecation notice" )
3649
3750
3851def test_uid_change (container ):
@@ -45,6 +58,9 @@ def test_uid_change(container):
4558 )
4659 # usermod is slow so give it some time
4760 rv = c .wait (timeout = 120 )
61+ logs = c .logs (stdout = True ).decode ("utf-8" )
62+ assert "ERROR" not in logs
63+ assert "WARNING" not in logs
4864 assert rv == 0 or rv ["StatusCode" ] == 0
4965 assert "uid=1010(jovyan)" in c .logs (stdout = True ).decode ("utf-8" )
5066
@@ -60,6 +76,8 @@ def test_gid_change(container):
6076 rv = c .wait (timeout = 10 )
6177 assert rv == 0 or rv ["StatusCode" ] == 0
6278 logs = c .logs (stdout = True ).decode ("utf-8" )
79+ assert "ERROR" not in logs
80+ assert "WARNING" not in logs
6381 assert "gid=110(jovyan)" in logs
6482 assert "groups=110(jovyan),100(users)" in logs
6583
@@ -79,6 +97,8 @@ def test_nb_user_change(container):
7997 time .sleep (10 )
8098 LOGGER .info (f"Checking if the user is changed to { nb_user } by the start script ..." )
8199 output = running_container .logs (stdout = True ).decode ("utf-8" )
100+ assert "ERROR" not in output
101+ assert "WARNING" not in output
82102 assert (
83103 f"username: jovyan -> { nb_user } " in output
84104 ), f"User is not changed to { nb_user } "
@@ -134,6 +154,8 @@ def test_chown_extra(container):
134154 rv = c .wait (timeout = 120 )
135155 assert rv == 0 or rv ["StatusCode" ] == 0
136156 logs = c .logs (stdout = True ).decode ("utf-8" )
157+ assert "ERROR" not in logs
158+ assert "WARNING" not in logs
137159 assert "/home/jovyan/.bashrc:1010:101" in logs
138160 assert "/opt/conda/bin/jupyter:1010:101" in logs
139161
@@ -156,6 +178,8 @@ def test_chown_home(container):
156178 rv = c .wait (timeout = 120 )
157179 assert rv == 0 or rv ["StatusCode" ] == 0
158180 logs = c .logs (stdout = True ).decode ("utf-8" )
181+ assert "ERROR" not in logs
182+ assert "WARNING" not in logs
159183 assert "/home/kitten/.bashrc:1010:101" in logs
160184
161185
@@ -169,7 +193,10 @@ def test_sudo(container):
169193 )
170194 rv = c .wait (timeout = 10 )
171195 assert rv == 0 or rv ["StatusCode" ] == 0
172- assert "uid=0(root)" in c .logs (stdout = True ).decode ("utf-8" )
196+ logs = c .logs (stdout = True ).decode ("utf-8" )
197+ assert "ERROR" not in logs
198+ assert "WARNING" not in logs
199+ assert "uid=0(root)" in logs
173200
174201
175202def test_sudo_path (container ):
@@ -183,6 +210,8 @@ def test_sudo_path(container):
183210 rv = c .wait (timeout = 10 )
184211 assert rv == 0 or rv ["StatusCode" ] == 0
185212 logs = c .logs (stdout = True ).decode ("utf-8" )
213+ assert "ERROR" not in logs
214+ assert "WARNING" not in logs
186215 assert logs .rstrip ().endswith ("/opt/conda/bin/jupyter" )
187216
188217
@@ -196,24 +225,75 @@ def test_sudo_path_without_grant(container):
196225 rv = c .wait (timeout = 10 )
197226 assert rv == 0 or rv ["StatusCode" ] == 0
198227 logs = c .logs (stdout = True ).decode ("utf-8" )
228+ assert "ERROR" not in logs
229+ assert "WARNING" not in logs
199230 assert logs .rstrip ().endswith ("/opt/conda/bin/jupyter" )
200231
201232
202- def test_group_add (container , tmpdir ):
233+ def test_group_add (container ):
203234 """Container should run with the specified uid, gid, and secondary
204- group.
235+ group. It won't be possible to modify /etc/passwd since gid is nonzero, so
236+ additionally verify that setting gid=0 is suggested in a warning.
205237 """
206238 c = container .run (
207239 user = "1010:1010" ,
208- group_add = ["users" ],
240+ group_add = ["users" ], # Ensures write access to /home/jovyan
209241 command = ["start.sh" , "id" ],
210242 )
211243 rv = c .wait (timeout = 5 )
212244 assert rv == 0 or rv ["StatusCode" ] == 0
213245 logs = c .logs (stdout = True ).decode ("utf-8" )
246+ assert "ERROR" not in logs
247+ warnings = [
248+ warning for warning in logs .split ("\n " ) if warning .startswith ("WARNING" )
249+ ]
250+ assert len (warnings ) == 1
251+ assert "Try setting gid=0" in warnings [0 ]
214252 assert "uid=1010 gid=1010 groups=1010,100(users)" in logs
215253
216254
255+ def test_set_uid (container ):
256+ """Container should run with the specified uid and NB_USER.
257+ The /home/jovyan directory will not be writable since it's owned by 1000:users.
258+ Additionally verify that "--group-add=users" is suggested in a warning to restore
259+ write access.
260+ """
261+ c = container .run (
262+ user = "1010" ,
263+ command = ["start.sh" , "id" ],
264+ )
265+ rv = c .wait (timeout = 5 )
266+ assert rv == 0 or rv ["StatusCode" ] == 0
267+ logs = c .logs (stdout = True ).decode ("utf-8" )
268+ assert "ERROR" not in logs
269+ assert "uid=1010(jovyan) gid=0(root)" in logs
270+ warnings = [
271+ warning for warning in logs .split ("\n " ) if warning .startswith ("WARNING" )
272+ ]
273+ assert len (warnings ) == 1
274+ assert "--group-add=users" in warnings [0 ]
275+
276+
277+ def test_set_uid_and_nb_user (container ):
278+ """Container should run with the specified uid and NB_USER."""
279+ c = container .run (
280+ user = "1010" ,
281+ environment = ["NB_USER=kitten" ],
282+ group_add = ["users" ], # Ensures write access to /home/jovyan
283+ command = ["start.sh" , "id" ],
284+ )
285+ rv = c .wait (timeout = 5 )
286+ assert rv == 0 or rv ["StatusCode" ] == 0
287+ logs = c .logs (stdout = True ).decode ("utf-8" )
288+ assert "ERROR" not in logs
289+ assert "uid=1010(kitten) gid=0(root)" in logs
290+ warnings = [
291+ warning for warning in logs .split ("\n " ) if warning .startswith ("WARNING" )
292+ ]
293+ assert len (warnings ) == 1
294+ assert "user is kitten but home is /home/jovyan" in warnings [0 ]
295+
296+
217297def test_container_not_delete_bind_mount (container , tmp_path ):
218298 """Container should not delete host system files when using the (docker)
219299 -v bind mount flag and mapping to /home/jovyan.
@@ -235,6 +315,9 @@ def test_container_not_delete_bind_mount(container, tmp_path):
235315 command = ["start.sh" , "ls" ],
236316 )
237317 rv = c .wait (timeout = 5 )
318+ logs = c .logs (stdout = True ).decode ("utf-8" )
319+ assert "ERROR" not in logs
320+ assert "WARNING" not in logs
238321 assert rv == 0 or rv ["StatusCode" ] == 0
239322 assert p .read_text () == "some-content"
240323 assert len (list (tmp_path .iterdir ())) == 1
@@ -264,4 +347,6 @@ def test_jupyter_env_vars_to_unset_as_root(container, enable_root):
264347 rv = c .wait (timeout = 10 )
265348 assert rv == 0 or rv ["StatusCode" ] == 0
266349 logs = c .logs (stdout = True ).decode ("utf-8" )
350+ assert "ERROR" not in logs
351+ assert "WARNING" not in logs
267352 assert "I like bananas and stuff, and love to keep secrets!" in logs
0 commit comments