Environment_Tests#

These tests are grouped into a single notebook called all_tests.ipynb which you can run using the ope test command. Running this command outputs the results of the tests, separating them into two groups of PASSED TESTS and FAILED TESTS. We keep track of these groups using the global variables ERRORS and PASSES which are initialized below.

# global var to keep track of test results we have
ERRORS = []
PASSES = []

Write Permission to Home Directory Test#

This test checks whether the user running the Jupyter Notebook has write permissions to their home directory. It does so by attempting to create a temporary file in the user’s home directory and then deleting it. If the test succeeds, it confirms that the user has write permissions to their home directory.

import subprocess
import os
def shelltest(CMD):
    global ERRORS
    try:
        result = subprocess.check_output(CMD, shell=True, stderr=subprocess.STDOUT)
        return 0, result.decode('utf-8')
    except subprocess.CalledProcessError as e:
        return e.returncode, e.output.decode('utf-8')
    except Exception as e:  # new add: handle exceptions 
        return -1, str(e)

# Test to check write permissions to home directory
TEST = "WRITE PERMISSION TO HOME DIRECTORY"
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'"

# Execute Test
e, output = shelltest(CMD)
if e == 0:
    PASSES.append("Write Permission to Home Directory test")
else:
    ERRORS.append(output)

Environmental Variables test#

The Environmental Variables test verifies that the environment variables set during the Dockerfile setup are correctly maintained within the Jupyter Notebook. Additionally, it checks if the user has a valid User ID (UID) and Group ID (GID) within a customizable range. This test ensures that the required environment variables are correctly set up and that the user’s UID and GID are within acceptable bounds.

import os

#Verify environment variables are correct

NB_UID = int(os.environ['NB_UID'])
NB_GID = int(os.environ['NB_GID'])
NB_GROUP = os.environ['NB_GROUP']

XDG_CACHE_HOME = f"/home/{NB_USER}/.cache/"

UID_LOWER_BOUND = 2000  
UID_UPPER_BOUND = 60000 

GID_LOWER_BOUND = 2000  
GID_UPPER_BOUND = 60000 

EXPECTED_NB_GROUP = 'root'

err = []

def check_environment_test():
    if not (UID_LOWER_BOUND <= NB_UID <= UID_UPPER_BOUND):
         err.append(f"NB_UID {NB_UID} is not within the acceptable range: {UID_LOWER_BOUND}-{UID_UPPER_BOUND}.")


    if not (GID_LOWER_BOUND <= NB_GID <= GID_UPPER_BOUND):
         err.append(f"NB_GID {NB_GID} is not within the acceptable range: {GID_LOWER_BOUND}-{GID_UPPER_BOUND}.")


    if  NB_GROUP != EXPECTED_NB_GROUP:
         err.append("NB_GROUP does not match " + "'" + EXPECTED_NB_GROUP + "'")


    if NB_USER != 'jovyan':
         err.append("NB_USER does not match 'jovyan'.")


    if XDG_CACHE_HOME != '/home/jovyan/.cache/':
         err.append("XDD_CACHE_HOME does not match expected path: /home/jovyan/.cache/")


    if len(err) != 0:
         err.append("Environmental Variables test")

    else:
        s = '; '.join(err)
        print("Environmental Variables test ERROR: " + s)
    
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[3], line 5
      1 import os
      3 #Verify environment variables are correct
----> 5 NB_UID = int(os.environ['NB_UID'])
      6 NB_GID = int(os.environ['NB_GID'])
      7 NB_GROUP = os.environ['NB_GROUP']

File ~/miniconda3/lib/python3.10/os.py:680, in _Environ.__getitem__(self, key)
    677     value = self._data[self.encodekey(key)]
    678 except KeyError:
    679     # raise KeyError with the original key value
--> 680     raise KeyError(key) from None
    681 return self.decodevalue(value)

KeyError: 'NB_UID'

Network Test#

This test ensures that the container has a connection to the internet. We use both ping and curl because some systems may not have one of these utilities by default—in our case, our cluster configuration did not include ping by default. If either ping or curl works properly, then the container is connected to the internet and the test passes, otherwise it fails.

# Curl test to check internet connectivity
e1, output = shelltest("curl google.com")
e2, output = shelltest("ping google.com")
if e1 == 0 or e2 == 0:
    PASSES.append("Network test")
else:
    ERRORS.append("Network test ERROR: " + output)

Pip-Conda Test#

The Pip-Conda Test verifies the ability to install Python packages using the pip package manager within the user’s home directory. In this specific case, it attempts to install the pytest package using pip. If the installation succeeds, the test passes, indicating that the user can install Python packages.

TEST = "PIP PACKAGE INSTALLATION"
CMD = "pip install --user pytest"
e, output = shelltest(CMD)

if e == 0:
    PASSES.append("Pip-Conda test")
else:
    ERRORS.append("Pip-Conda test ERROR: " + output)

Git and SSH Test#

The Git and SSH Test checks whether the Git and SSH configurations are correctly set up within the Jupyter Notebook environment. It verifies the paths to the .gitconfig and ssh_config files and reports whether they exist and can be read. This test helps ensure that Git and SSH are properly configured for the user.

e, output = shelltest("readlink -f ~/.gitconfig")

if e == 0:
    PASSES.append("Git config test")
else:
    ERRORS.append("Git config test ERROR" + output)
e, output = shelltest("readlink -f /etc/ssh/ssh_config")

if e == 0:
    PASSES.append("ssh config test")
else:
    ERRORS.append("ssh config test ERROR" + output)

Conda Directory Test#

The Conda Directory Test evaluates the read and write permissions of Conda directories. It checks if the Conda base directory and Conda environment directory are both readable and writable. If these directories have the necessary permissions, the test passes, indicating that Conda can be used effectively within the environment.

def check_permissions(dir_path):
    """Check if a directory is readable and writable."""
    global ERRORS
    try:
        readable = os.access(dir_path, os.R_OK)
        writable = os.access(dir_path, os.W_OK)
        return readable, writable
    except Exception as e:
        ERRORS.append("Conda directory r/w test" + f"checking permissions for {dir_path}.")
        return False, False

# Identify the conda directories
conda_base_dir = os.path.abspath(os.path.join(os.path.dirname(os.sys.executable), ".."))
conda_env_dir = os.environ.get('CONDA_PREFIX', '')

unaccessible_dirs = 0

for dir_name, dir_path in [('Conda Base Directory', conda_base_dir), ('Conda Environment Directory', conda_env_dir)]:
    readable, writable = check_permissions(dir_path)

    if not (readable and writable):
        unaccessible_dirs += 1
if unaccessible_dirs == 0:
    PASSES.append("Conda directory r/w test")
if len(ERRORS) > 0:
    print("FAILED TESTS:")
    print('-' + '\n-'.join(ERRORS))
    print('\n')
    print("PASSED TESTS:")
    print('-' + '\n-'.join(PASSES))
else:
    print("ALL TESTS PASS")
    print('-' + '\n-'.join(PASSES))
FAILED TESTS:
-ASLR test ERROR: ASLR status is enabled with status 2 (0 is disabled, 1 is partial and 2 is full)
-Network test ERROR: /bin/sh: 1: curl: not found



PASSED TESTS:
-Write Permission to Home Directory test
-Pip-Conda test
-Git config test
-ssh config test

Additional tests can be added to suit the compatability needs of users and textbooks. They may be written in this format:

# Define the test and conditions

test_command = "<INSERT TEST COMMAND HERE>"
expected_result = "<INSERT EXPECTED RESULT HERE>"
environment_variables = "<INSERT REQUIRED VARIABLES IF APPLICABLE>"

def perform_test(command, expected_result):

    execution_result, output = shelltest(command)

    if output == expected_result:
        # If test passes, add to the PASSES array 
        PASSES.append("<TEST NAME> PASSED")
    else:
        # If test fails, add to the ERRORS array with error message
        ERRORS.append("<TEST NAME> FAILED: " + output)

# Run test to verify success
perform_test(test_command, expected_result)

Test Authors: Ross Mikulskis, Jonathan Mikalov, Yuxie Ge, Yiqin Zhang, Riya Deokar