diff --git a/testing/All-tests.ipynb b/testing/All-tests.ipynb new file mode 100644 index 0000000..05e22c6 --- /dev/null +++ b/testing/All-tests.ipynb @@ -0,0 +1,350 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bbdfb803-23f2-406f-9f85-ebcea5b54d13", + "metadata": { + "user_expressions": [] + }, + "source": [ + "### Environment test" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6276ae14-2a64-4d96-bc7d-b5d7f24de9cc", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Match\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "#Compare UID and GID from Operating System and notebook, make sure they match\n", + "real_uid = os.getuid()\n", + "notebook_uid = !id -u\n", + "\n", + "real_gid = os.getgid()\n", + "notebook_gid = !id -g\n", + "\n", + "if real_uid != int(notebook_uid[0]) or real_gid != int(notebook_gid[0]):\n", + " print(\"No match\")\n", + " print(real_uid, notebook_uid, real_gid, notebook_gid)\n", + "else:\n", + " print(\"Match\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cb52918-b1c4-4b4b-b8ae-bd2298717380", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "#Verify environment variables are correct\n", + "\n", + "OPE_UID = int(os.environ['OPE_UID'])\n", + "OPE_GID = int(os.environ['OPE_GID'])\n", + "OPE_GROUP = os.environ['OPE_GROUP']\n", + "NB_GROUP = os.environ['NB_GROUP'] \n", + "NB_USER = os.environ['NB_USER']\n", + "XDG_CACHE_HOME = f\"/home/{NB_USER}/.cache/\"\n", + "\n", + "\n", + "UID_LOWER_BOUND = 2000 \n", + "UID_UPPER_BOUND = 60000 \n", + "\n", + "GID_LOWER_BOUND = 2000 \n", + "GID_UPPER_BOUND = 60000 \n", + "\n", + "def check_environment_test():\n", + "\n", + " err = []\n", + " \n", + " if not (UID_LOWER_BOUND <= OPE_UID <= UID_UPPER_BOUND):\n", + " err.append(f\"OPE_UID {OPE_UID} is not within the acceptable range: {UID_LOWER_BOUND}-{UID_UPPER_BOUND}.\")\n", + "\n", + "\n", + " if not (GID_LOWER_BOUND <= OPE_GID <= GID_UPPER_BOUND):\n", + " err.append(f\"OPE_GID {OPE_GID} is not within the acceptable range: {GID_LOWER_BOUND}-{GID_UPPER_BOUND}.\")\n", + "\n", + " \n", + " if OPE_GROUP != 'root' or NB_GROUP != 'root':\n", + " err.append(\"OPE_GROUP Or NB_GROUP does not match 'root'.\")\n", + "\n", + " \n", + " if NB_USER != 'jovyan':\n", + " err.append(\"NB_USER does not match 'jovyan'.\")\n", + "\n", + " \n", + " if XDG_CACHE_HOME != '/home/jovyan/.cache/':\n", + " err.append(\"XDD_CACHE_HOME does not match expected path: /home/jovyan/.cache/\")\n", + "\n", + " \n", + " if len(err) != 0:\n", + " PASSES.append(\"Environmental Variables test\")\n", + " \n", + " else:\n", + " s = '; '.join(err)\n", + " ERRORS.append(\"Environmental Variables test ERROR: \" + s)\n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2c4ee3f-9372-4f6d-97d8-09ad71b52d5f", + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "# ASLR test: run gdb 100 times on date binary stopping at first instruction and grep ld. If ld is found at same address then ASLR is properly disabled.\n", + "# We want to disable ASLR so that students can use GDB properly in their environments.\n", + "count=$(for ((i=0;i<100; i++)); do gdb -ex starti -ex quit -q --batch /usr/bin/date 2>/dev/null | grep ld; done | tee out | uniq | wc -l)\n", + "if [[ $count == 1 ]]; then\n", + " echo \"ASLR test: ASLR is disabled\"\n", + "else\n", + " aslr_status=$(cat /proc/sys/kernel/randomize_va_space)\n", + " if [[ $aslr_status == 2 ]]; then\n", + " echo \"ASLR test: ERROR: Full ASLR is enabled\"\n", + " elif [[ $aslr_status == 1 ]]; then\n", + " echo \"ASLR test: ERROR: Partial ASLR is enabled\"\n", + " else\n", + " echo \"ASLR test: ASLR is disabled\"\n", + " fi\n", + "fi" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "596d4b84-8366-44c4-833f-3f04fbb521a8", + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "# Ping test to check internet connectivity\n", + "ping -c 3 google.com > /dev/null 2>&1\n", + "# if zero exit code from above command, ping is successful\n", + "if [[ $? == 0 ]]; then\n", + " echo \"Ping test: OK: Internet connection is working.\"\n", + "else\n", + " echo \"Ping test: ERROR: Internet connection is not working.\"\n", + "fi" + ] + }, + { + "cell_type": "markdown", + "id": "0f31f64a-e62a-43f6-b5d7-1efe943ae2bf", + "metadata": { + "user_expressions": [] + }, + "source": [ + "### Write Permission to Home Directory Test" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "420d81be-46f8-4c72-b2e3-e52b3774ee09", + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import os\n", + "\n", + "def shelltest(CMD):\n", + " try:\n", + " result = subprocess.check_output(CMD, shell=True, stderr=subprocess.STDOUT)\n", + " return 0, result.decode('utf-8')\n", + " except subprocess.CalledProcessError as e:\n", + " return e.returncode, e.output.decode('utf-8')\n", + " except Exception as e: # new add: handle exceptions \n", + " return -1, str(e)\n", + "\n", + "# Test to check write permissions to home directory\n", + "TEST = \"WRITE PERMISSION TO HOME DIRECTORY\"\n", + "CMD = f\"touch {os.path.expanduser('~')}/test_write_permissions.tmp && echo 'Write Permission: Yes' && rm {os.path.expanduser('~')}/test_write_permissions.tmp || echo 'Write Permission: No'\"\n", + "\n", + "# Execute Test\n", + "ERRORS, output = shelltest(CMD)\n", + "print(f\"Output of {TEST}:\\n{output}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f1f955c-e0b7-4dc7-9876-7a311b325503", + "metadata": {}, + "outputs": [], + "source": [ + "if 'Write Permission: No' in output:\n", + " ERRORS += 1\n", + "if ERRORS > 0:\n", + " print(\"THERE ARE \" + str(ERRORS) + \" ERRORS\")\n", + "else:\n", + " print(\"ALL TESTS PASS\")\n", + "print(\"ERRORS:\", ERRORS)" + ] + }, + { + "cell_type": "markdown", + "id": "97e1fb29-4eaa-465a-8780-2d7a8a4e9721", + "metadata": { + "user_expressions": [] + }, + "source": [ + "### Pip-Conda Test" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30b5e040-69e5-42ec-ba13-095624c0a38f", + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import os\n", + "import sys\n", + "import logging\n", + "\n", + "# Set up logging\n", + "logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')\n", + "logger = logging.getLogger()\n", + "\n", + "def shelltest(CMD):\n", + " \"\"\"Executes a shell command and returns the result.\"\"\"\n", + " try:\n", + " result = subprocess.check_output(CMD, shell=True, stderr=subprocess.STDOUT)\n", + " return 0, result.decode('utf-8')\n", + " except subprocess.CalledProcessError as e:\n", + " logger.error(\"Shell command failed with CalledProcessError: %s\", str(e))\n", + " return e.returncode, e.output.decode('utf-8')\n", + " except Exception as e:\n", + " logger.exception(\"Unexpected error when running shell command.\")\n", + " return -1, str(e)\n", + "\n", + "ERRORS = 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14741788-0eb4-4032-a915-d49305ef8b25", + "metadata": {}, + "outputs": [], + "source": [ + "TEST = \"PIP PACKAGE INSTALLATION\"\n", + "CMD = \"pip install --user pytest || echo 'PIP Install: No'\"\n", + "ret_code, output = shelltest(CMD)\n", + "logger.info(f\"Output of {TEST}:\\n{output}\")\n", + "\n", + "if 'PIP Install: No' in output or ret_code != 0:\n", + " ERRORS += 1\n", + " logger.error(\"PIP package installation failed.\")" + ] + }, + { + "cell_type": "markdown", + "id": "43e44066-ddeb-48b8-a74c-ac010070fbcb", + "metadata": { + "user_expressions": [] + }, + "source": [ + "### Test if conda directories are read/writable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f39267bc-fdcc-46ba-984f-84c1c4f87a7d", + "metadata": {}, + "outputs": [], + "source": [ + "def check_permissions(dir_path):\n", + " \"\"\"Check if a directory is readable and writable.\"\"\"\n", + " try:\n", + " readable = os.access(dir_path, os.R_OK)\n", + " writable = os.access(dir_path, os.W_OK)\n", + " return readable, writable\n", + " except Exception as e:\n", + " logger.exception(f\"Error checking permissions for {dir_path}.\")\n", + " return False, False\n", + "\n", + "# Identify the conda directories\n", + "conda_base_dir = os.path.abspath(os.path.join(os.path.dirname(os.sys.executable), \"..\"))\n", + "conda_env_dir = os.environ.get('CONDA_PREFIX', '')\n", + "\n", + "unaccessible_dirs = 0\n", + "\n", + "# Ensure conda directories are available\n", + "if not (conda_base_dir or conda_env_dir):\n", + " logger.error(\"Conda directory not found. Skipping test.\")\n", + " ERRORS += 1\n", + "\n", + "for dir_name, dir_path in [('Conda Base Directory', conda_base_dir), \n", + " ('Conda Environment Directory', conda_env_dir)]:\n", + " readable, writable = check_permissions(dir_path)\n", + " logger.info(f\"{dir_name}: {dir_path}\")\n", + " logger.info(f\"Readable: {readable}\")\n", + " logger.info(f\"Writable: {writable}\\n\")\n", + "\n", + " if not (readable and writable):\n", + " unaccessible_dirs += 1\n", + "\n", + "if unaccessible_dirs == 2:\n", + " ERRORS += 1\n", + " logger.error(\"Both Conda directories are not fully accessible.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52528d0e-1b7b-4565-a4a6-a47eba0086b7", + "metadata": {}, + "outputs": [], + "source": [ + "if ERRORS > 0:\n", + " logging.error(f\"THERE ARE {ERRORS} ERRORS\")\n", + "else:\n", + " logging.info(\"ALL TESTS PASS\")\n", + "\n", + "logging.info(f\"ERRORS: {ERRORS}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/testing/README.md b/testing/README.md new file mode 100644 index 0000000..6fcda2d --- /dev/null +++ b/testing/README.md @@ -0,0 +1,4 @@ +This is a test jupyter notebook to be run with the +following ope command in order to verify the ope container +environment is properly set up: + ope test