Skip to content

Commit 1fee387

Browse files
authored
refactor(tests/scripts): modularize _get_notebook_id for reuse and add pytest coverage for manifest file validation (#2497)
1 parent e0e2fa3 commit 1fee387

File tree

6 files changed

+141
-23
lines changed

6 files changed

+141
-23
lines changed

ci/__init__.py

Whitespace-only changes.

ci/cached-builds/__init__.py

Whitespace-only changes.

ci/cached-builds/make_test.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ def gha_log_group(title):
165165
# https://docs.python.org/3/library/unittest.mock-examples.html#patch-decorators
166166
@unittest.mock.patch("time.sleep", unittest.mock.Mock())
167167
class TestMakeTest(unittest.TestCase):
168-
@unittest.mock.patch("make_test.execute")
168+
target = execute.__module__ + "." + execute.__name__
169+
170+
@unittest.mock.patch(target)
169171
def test_make_commands_jupyter(self, mock_execute: unittest.mock.Mock) -> None:
170172
"""Compares the commands with what we had in the openshift/release yaml"""
171173
run_tests("jupyter-minimal-ubi9-python-3.11")
@@ -174,7 +176,7 @@ def test_make_commands_jupyter(self, mock_execute: unittest.mock.Mock) -> None:
174176
assert "make test-jupyter-minimal-ubi9-python-3.11" in commands
175177
assert "make undeploy9-jupyter-minimal-ubi9-python-3.11" in commands
176178

177-
@unittest.mock.patch("make_test.execute")
179+
@unittest.mock.patch(target)
178180
def test_make_commands_jupyter_rocm(self, mock_execute: unittest.mock.Mock) -> None:
179181
"""Compares the commands with what we had in the openshift/release yaml"""
180182
run_tests("rocm-jupyter-tensorflow-ubi9-python-3.11")
@@ -183,7 +185,7 @@ def test_make_commands_jupyter_rocm(self, mock_execute: unittest.mock.Mock) -> N
183185
assert "make test-jupyter-rocm-tensorflow-ubi9-python-3.11" in commands
184186
assert "make undeploy9-jupyter-rocm-tensorflow-ubi9-python-3.11" in commands
185187

186-
@unittest.mock.patch("make_test.execute")
188+
@unittest.mock.patch(target)
187189
def test_make_commands_codeserver(self, mock_execute: unittest.mock.Mock) -> None:
188190
"""Compares the commands with what we had in the openshift/release yaml"""
189191
run_tests("codeserver-ubi9-python-3.11")
@@ -192,7 +194,7 @@ def test_make_commands_codeserver(self, mock_execute: unittest.mock.Mock) -> Non
192194
assert "make validate-codeserver-image image=codeserver-ubi9-python-3.11" in commands
193195
assert "make undeploy9-codeserver-ubi9-python-3.11" in commands
194196

195-
@unittest.mock.patch("make_test.execute")
197+
@unittest.mock.patch(target)
196198
def test_make_commands_rstudio(self, mock_execute: unittest.mock.Mock) -> None:
197199
"""Compares the commands with what we had in the openshift/release yaml"""
198200
run_tests("rstudio-c9s-python-3.11")
@@ -201,7 +203,7 @@ def test_make_commands_rstudio(self, mock_execute: unittest.mock.Mock) -> None:
201203
assert "make validate-rstudio-image image=rstudio-c9s-python-3.11" in commands
202204
assert "make undeploy-c9s-rstudio-c9s-python-3.11" in commands
203205

204-
@unittest.mock.patch("make_test.execute")
206+
@unittest.mock.patch(target)
205207
def test_make_commands_rsudio_rhel(self, mock_execute: unittest.mock.Mock) -> None:
206208
"""Compares the commands with what we had in the openshift/release yaml"""
207209
run_tests("rstudio-rhel9-python-3.11")
@@ -210,7 +212,7 @@ def test_make_commands_rsudio_rhel(self, mock_execute: unittest.mock.Mock) -> No
210212
assert "make validate-rstudio-image image=rstudio-rhel9-python-3.11" in commands
211213
assert "make undeploy-rhel9-rstudio-rhel9-python-3.11" in commands
212214

213-
@unittest.mock.patch("make_test.execute")
215+
@unittest.mock.patch(target)
214216
def test_make_commands_cuda_rstudio(self, mock_execute: unittest.mock.Mock) -> None:
215217
"""Compares the commands with what we had in the openshift/release yaml"""
216218
run_tests("cuda-rstudio-c9s-python-3.11")
@@ -219,7 +221,7 @@ def test_make_commands_cuda_rstudio(self, mock_execute: unittest.mock.Mock) -> N
219221
assert "make validate-rstudio-image image=cuda-rstudio-c9s-python-3.11" in commands
220222
assert "make undeploy-c9s-rstudio-c9s-python-3.11" in commands
221223

222-
@unittest.mock.patch("make_test.execute")
224+
@unittest.mock.patch(target)
223225
def test_make_commands_cuda_rstudio_rhel(self, mock_execute: unittest.mock.Mock) -> None:
224226
"""Compares the commands with what we had in the openshift/release yaml"""
225227
run_tests("cuda-rstudio-rhel9-python-3.11")
@@ -228,7 +230,7 @@ def test_make_commands_cuda_rstudio_rhel(self, mock_execute: unittest.mock.Mock)
228230
assert "make validate-rstudio-image image=cuda-rstudio-rhel9-python-3.11" in commands
229231
assert "make undeploy-rhel9-rstudio-rhel9-python-3.11" in commands
230232

231-
@unittest.mock.patch("make_test.execute")
233+
@unittest.mock.patch(target)
232234
def test_make_commands_runtime(self, mock_execute: unittest.mock.Mock) -> None:
233235
"""Compares the commands with what we had in the openshift/release yaml"""
234236
run_tests("runtime-datascience-ubi9-python-3.11")
@@ -237,7 +239,7 @@ def test_make_commands_runtime(self, mock_execute: unittest.mock.Mock) -> None:
237239
assert "make validate-runtime-image image=runtime-datascience-ubi9-python-3.11" in commands
238240
assert "make undeploy9-runtimes-datascience-ubi9-python-3.11" in commands
239241

240-
@unittest.mock.patch("make_test.execute")
242+
@unittest.mock.patch(target)
241243
def test_make_commands_rocm_runtime(self, mock_execute: unittest.mock.Mock) -> None:
242244
"""Compares the commands with what we had in the openshift/release yaml"""
243245
run_tests("rocm-runtime-pytorch-ubi9-python-3.11")

ci/cached_builds

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cached-builds

scripts/test_jupyter_with_papermill.sh

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,7 @@ function _test_datascience_notebook()
335335
_run_test "${jupyter_datascience_notebook_id}"
336336
}
337337

338-
# Description:
339-
# "Orchestration" function computes necessary parameters and prepares the running notebook workload for papermill tests to be invoked
340-
# - notebook_id is calculated based on the workload name and computed accelerator value
341-
# - Appropriate "source of truth" file to be used in asserting package version is copied into the running pod
342-
# - papermill is installed on the running pod
343-
# - All relevant tests based on the notebook_id are invoked
344-
function _handle_test()
345-
{
338+
function _get_notebook_id() {
346339
local notebook_id=
347340

348341
# Due to existing logic - cuda accelerator value needs to be treated as empty string
@@ -372,6 +365,20 @@ function _handle_test()
372365
;;
373366
esac
374367

368+
echo "${notebook_id}"
369+
}
370+
371+
# Description:
372+
# "Orchestration" function computes necessary parameters and prepares the running notebook workload for papermill tests to be invoked
373+
# - notebook_id is calculated based on the workload name and computed accelerator value
374+
# - Appropriate "source of truth" file to be used in asserting package version is copied into the running pod
375+
# - papermill is installed on the running pod
376+
# - All relevant tests based on the notebook_id are invoked
377+
function _handle_test()
378+
{
379+
local notebook_id=
380+
notebook_id=$(_get_notebook_id)
381+
375382
_create_test_versions_source_of_truth "${notebook_id}"
376383

377384
"${kbin}" exec "${notebook_workload_name}" -- /bin/sh -c "python3 -m pip install papermill"
@@ -413,10 +420,15 @@ if ! [ -e "${yqbin}" ]; then
413420
exit 1
414421
fi
415422

416-
printf '%s\n' "Waiting for ${notebook_name} workload to be ready. This could take a few minutes..."
417-
_wait_for_workload "${notebook_name}"
423+
function main() {
424+
printf '%s\n' "Waiting for ${notebook_name} workload to be ready. This could take a few minutes..."
425+
_wait_for_workload "${notebook_name}"
418426

419-
notebook_workload_name=$("${kbin}" get pods -l app="${notebook_name}" -o jsonpath='{.items[0].metadata.name}')
427+
notebook_workload_name=$("${kbin}" get pods -l app="${notebook_name}" -o jsonpath='{.items[0].metadata.name}')
420428

421-
_handle_test
429+
_handle_test
430+
}
422431

432+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
433+
main "$@"
434+
fi

tests/manifests.py

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@
33

44
import dataclasses
55
import enum
6+
import shlex
67
import shutil
7-
import unittest
8+
import subprocess
9+
import sys
10+
import typing
811
from pathlib import Path
912

13+
import pytest
14+
15+
if typing.TYPE_CHECKING:
16+
from collections.abc import Generator, Iterable
17+
18+
ROOT_DIR = Path(__file__).parent.parent
19+
1020
JUPYTER_MINIMAL_NOTEBOOK_ID = "minimal"
1121
JUPYTER_DATASCIENCE_NOTEBOOK_ID = "datascience"
1222
JUPYTER_TRUSTYAI_NOTEBOOK_ID = "trustyai"
@@ -192,7 +202,7 @@ def get_source_of_truth_filepath(
192202
return filepath
193203

194204

195-
class SelfTests(unittest.TestCase):
205+
class TestManifests:
196206
def test_rstudio_path(self):
197207
metadata = extract_metadata_from_path(Path("notebooks/rstudio/rhel9-python-3.11"))
198208
assert metadata == NotebookMetadata(
@@ -261,3 +271,96 @@ def test_source_of_truth_jupyter_tensorflow_rocm(self):
261271
metadata = extract_metadata_from_path(Path("notebooks/jupyter/rocm/tensorflow/ubi9-python-3.12"))
262272
path = get_source_of_truth_filepath(root_repo_directory=Path("notebooks"), metadata=metadata)
263273
assert path == Path("notebooks/manifests/base/jupyter-rocm-tensorflow-notebook-imagestream.yaml")
274+
275+
def run_shell_function(
276+
self,
277+
shell_script_path: Path,
278+
shell_function_name: str,
279+
args: Iterable[str] = (),
280+
env: dict[str, str] | None = None,
281+
) -> str:
282+
env = env or {}
283+
shell_notebook_id = subprocess.run(
284+
f"""source {shell_script_path} && {shell_function_name} {" ".join(shlex.quote(arg) for arg in args)}""",
285+
shell=True,
286+
executable="/bin/bash",
287+
env=env,
288+
stdout=subprocess.PIPE,
289+
text=True,
290+
check=True,
291+
)
292+
return shell_notebook_id.stdout.rstrip()
293+
294+
@staticmethod
295+
def get_targets() -> Generator[tuple[str, Path], None, None]:
296+
# TODO(jdanek): should systematize import paths to avoid this hack
297+
sys.path.insert(0, str(ROOT_DIR / "ci/cached-builds"))
298+
from ci.cached_builds import gen_gha_matrix_jobs # noqa: PLC0415
299+
300+
python_311 = gen_gha_matrix_jobs.extract_image_targets(ROOT_DIR, env={"RELEASE_PYTHON_VERSION": "3.11"})
301+
python_312 = gen_gha_matrix_jobs.extract_image_targets(ROOT_DIR, env={"RELEASE_PYTHON_VERSION": "3.12"})
302+
targets = python_311 + python_312
303+
expected_manifest_paths = {
304+
"jupyter-minimal-ubi9-python-3.12": ROOT_DIR / "manifests/base/jupyter-minimal-notebook-imagestream.yaml",
305+
"runtime-minimal-ubi9-python-3.12": ROOT_DIR / "manifests/base/jupyter-minimal-notebook-imagestream.yaml",
306+
# no -gpu-?
307+
"cuda-jupyter-minimal-ubi9-python-3.12": ROOT_DIR
308+
/ "manifests/base/jupyter-minimal-notebook-imagestream.yaml",
309+
"rocm-jupyter-minimal-ubi9-python-3.12": ROOT_DIR
310+
/ "manifests/base/jupyter-minimal-notebook-imagestream.yaml",
311+
"jupyter-datascience-ubi9-python-3.12": ROOT_DIR
312+
/ "manifests/base/jupyter-datascience-notebook-imagestream.yaml",
313+
"runtime-datascience-ubi9-python-3.12": ROOT_DIR
314+
/ "manifests/base/jupyter-datascience-notebook-imagestream.yaml",
315+
"cuda-jupyter-pytorch-ubi9-python-3.12": ROOT_DIR
316+
/ "manifests/base/jupyter-pytorch-notebook-imagestream.yaml",
317+
"runtime-cuda-pytorch-ubi9-python-3.12": ROOT_DIR
318+
/ "manifests/base/jupyter-pytorch-notebook-imagestream.yaml",
319+
"rocm-jupyter-pytorch-ubi9-python-3.12": ROOT_DIR
320+
/ "manifests/base/jupyter-pytorch-notebook-imagestream.yaml",
321+
"rocm-runtime-pytorch-ubi9-python-3.12": ROOT_DIR
322+
/ "manifests/base/jupyter-pytorch-notebook-imagestream.yaml",
323+
"cuda-jupyter-pytorch-llmcompressor-ubi9-python-3.12": ROOT_DIR
324+
/ "manifests/base/jupyter-pytorch-notebook-imagestream.yaml",
325+
"runtime-cuda-pytorch-llmcompressor-ubi9-python-3.12": ROOT_DIR
326+
/ "manifests/base/jupyter-pytorch-notebook-imagestream.yaml",
327+
"cuda-jupyter-tensorflow-ubi9-python-3.12": ROOT_DIR
328+
/ "manifests/base/jupyter-tensorflow-notebook-imagestream.yaml",
329+
"runtime-cuda-tensorflow-ubi9-python-3.12": ROOT_DIR
330+
/ "manifests/base/jupyter-tensorflow-notebook-imagestream.yaml",
331+
"rocm-jupyter-tensorflow-ubi9-python-3.12": ROOT_DIR
332+
/ "manifests/base/jupyter-tensorflow-notebook-imagestream.yaml",
333+
"rocm-runtime-tensorflow-ubi9-python-3.12": ROOT_DIR
334+
/ "manifests/base/jupyter-tensorflow-notebook-imagestream.yaml",
335+
"jupyter-trustyai-ubi9-python-3.12": ROOT_DIR / "manifests/base/jupyter-trustyai-notebook-imagestream.yaml",
336+
"codeserver-ubi9-python-3.12": ROOT_DIR / "manifests/base/code-server-notebook-imagestream.yaml",
337+
"rstudio-ubi9-python-3.11": ROOT_DIR / "manifests/base/rstudio-buildconfig.yaml",
338+
"rstudio-c9s-python-3.11": ROOT_DIR / "manifests/base/rstudio-buildconfig.yaml",
339+
"cuda-rstudio-c9s-python-3.11": ROOT_DIR / "manifests/base/cuda-rstudio-buildconfig.yaml",
340+
"rstudio-rhel9-python-3.11": ROOT_DIR / "manifests/base/rstudio-buildconfig.yaml",
341+
"cuda-rstudio-rhel9-python-3.11": ROOT_DIR / "manifests/base/cuda-rstudio-buildconfig.yaml",
342+
}
343+
for target in targets:
344+
if "codeserver" in target:
345+
continue
346+
if "rstudio" in target:
347+
continue
348+
yield target, expected_manifest_paths[target]
349+
350+
@pytest.mark.parametrize("target,expected_manifest_path", get_targets())
351+
def test_compare_with_shell_implementation(self, target: str, expected_manifest_path: Path):
352+
shell_script_path = ROOT_DIR / "scripts/test_jupyter_with_papermill.sh"
353+
354+
notebook_id = self.run_shell_function(
355+
shell_script_path,
356+
"_get_notebook_id",
357+
env={"notebook_workload_name": target},
358+
)
359+
assert notebook_id
360+
361+
source_of_truth_filepath = self.run_shell_function(
362+
shell_script_path,
363+
"_get_source_of_truth_filepath",
364+
[notebook_id],
365+
)
366+
assert source_of_truth_filepath == str(expected_manifest_path)

0 commit comments

Comments
 (0)