1# Original code: 2# Copyright (c) Jupyter Development Team. 3# Distributed under the terms of the Modified BSD License. 4import time 5 6import pytest 7 8PROG_LIST_TO_CHECK = ( 9 "afni", 10 "RetroTS.py", 11 "align_epi_anat.py", 12 "3dinfo", 13 ) 14 15def test_uid_change(container): 16 """Container should change the UID of the default user.""" 17 c = container.run( 18 tty=True, 19 user='root', 20 environment=['CONTAINER_UID=1010'], 21 command=['start.sh', 'bash', '-c', 'id && touch /home/afni_user/test-file'] 22 ) 23 # usermod is slow so give it some time 24 c.wait(timeout=120) 25 stdout = c.logs(stdout=True).decode('utf-8') 26 if 'uid=1010(afni_user)' not in stdout: 27 print(stdout) 28 raise EnvironmentError( 29 "User id is not being changed correctly" 30 ) 31 32 33 34def test_gid_change(container): 35 """Container should change the GID of the default user.""" 36 c = container.run( 37 tty=True, 38 user='root', 39 environment=['CONTAINER_GID=110'], 40 command=['start.sh', 'id'] 41 ) 42 c.wait(timeout=10) 43 logs = c.logs(stdout=True).decode('utf-8') 44 assert 'gid=110(afni_user)' in logs 45 assert 'groups=110(afni_user),100(users)' in logs 46 47 48def test_chown_extra(container): 49 """Container should change the UID/GID of CHOWN_EXTRA.""" 50 c = container.run( 51 tty=True, 52 user='root', 53 environment=['CONTAINER_UID=1010', 54 'CONTAINER_GID=101', 55 'CHOWN_EXTRA=/usr/bin', 56 'CHOWN_EXTRA_OPTS=-R', 57 ], 58 command=['start.sh', 'bash', '-c', 'stat -c \'%n:%u:%g\' /usr/bin/python'] 59 ) 60 # chown is slow so give it some time 61 c.wait(timeout=5) 62 if not '/usr/bin/python:1010:101' in c.logs(stdout=True).decode('utf-8'): 63 print(c.logs(stdout=True).decode('utf-8')) 64 raise EnvironmentError( 65 "Expected to find an executable python in the container" 66 ) 67 68 69def test_chown_home(container): 70 """Container should change the CONTAINER_USER home directory owner and 71 group to the current value of CONTAINER_UID and CONTAINER_GID.""" 72 c = container.run( 73 tty=True, 74 user='root', 75 environment=['CONTAINER_UID=1010', 76 'CONTAINER_GID=101', 77 'CHOWN_HOME=yes', 78 'CHOWN_HOME_OPTS=-R', 79 ], 80 command=['start.sh', 'bash', '-c', 'stat -c \'%n:%u:%g\' /home/afni_user'] 81 ) 82 c.wait(timeout=2) 83 assert "Changing ownership of /home/afni_user to 1010:101 with options '-R'" in c.logs(stdout=True).decode('utf-8') 84 85 86def test_sudo(container): 87 """Container should grant passwordless sudo to the default user.""" 88 c = container.run( 89 tty=True, 90 user='root', 91 environment=['GRANT_SUDO=yes'], 92 command=['start.sh', 'sudo', 'id'] 93 ) 94 rv = c.wait(timeout=10) 95 assert rv == 0 or rv["StatusCode"] == 0 96 assert 'uid=0(root)' in c.logs(stdout=True).decode('utf-8') 97 98 99def test_sudo_path(container): 100 """Container should have usable /usr/local/bin in the sudo secure_path.""" 101 c = container.run( 102 tty=True, 103 user='root', 104 environment=['GRANT_SUDO=yes'], 105 command=['start.sh', 'sudo', 'which', 'apt-get'] 106 ) 107 rv = c.wait(timeout=10) 108 assert rv == 0 or rv["StatusCode"] == 0 109 assert c.logs(stdout=True).decode('utf-8').rstrip().endswith('/usr/local/bin/apt-get') 110 111 112def test_sudo_path_without_grant(container): 113 """Container should have usable /usr/local/bin in the unprivileged user""" 114 c = container.run( 115 tty=True, 116 user='root', 117 command=['start.sh', 'which', 'apt-get'] 118 ) 119 rv = c.wait(timeout=10) 120 assert rv == 0 or rv["StatusCode"] == 0 121 assert c.logs(stdout=True).decode('utf-8').rstrip().endswith('/usr/local/bin/apt-get') 122 123 124def test_group_add(container, tmpdir): 125 """Container should run with the specified uid, gid, and secondary 126 group. 127 """ 128 c = container.run( 129 user='1010:1010', 130 group_add=['users'], 131 command=['start.sh', 'id'] 132 ) 133 rv = c.wait(timeout=5) 134 assert rv == 0 or rv["StatusCode"] == 0 135 expected = 'uid=1010 gid=1010 groups=1010,100(users)' 136 assert expected in c.logs(stdout=True).decode('utf-8') 137 138@pytest.mark.parametrize( 139 "env_tweaks", 140 ( 141 ['GRANT_SUDO=yes'], 142 ['CONTAINER_UID=1010'], 143 ['CONTAINER_UID=1010','CONTAINER_GID=120','CHOWN_EXTRA=/opt','CHOWN_EXTRA_OPTS=-R'], 144 ) 145) 146@pytest.mark.parametrize( 147 "named_container", 148 ( 149 "afni/afni_make_build", 150 "afni/afni_cmake_build", 151 ), 152 indirect=True, 153) 154def test_various_programs_are_found(named_container,env_tweaks): 155 """Working containers should be able to find the installed binaries, scripts,etc""" 156 157 c = named_container.run( 158 tty=True, 159 user='root', 160 environment=[*env_tweaks], 161 detach=True 162 ) 163 # rv = c.wait(timeout=120) 164 # assert rv == 0 or rv["StatusCode"] == 0 165 # Check that each program is available (on the PATH and executable) 166 for prog in PROG_LIST_TO_CHECK: 167 res = c.exec_run(["which", prog]) 168 if not res.output.decode('utf-8').rstrip().endswith(prog): 169 print(res.output.decode('utf-8').rstrip()) 170 raise EnvironmentError(f"Could not find the '{prog}' executable in {named_container.image_name}") 171 172 173 174@pytest.mark.parametrize( "named_container", ( "afni/afni_make_build", 175 "afni/afni_cmake_build",), 176 indirect=True,) 177def test_singularity_like_restrictions(named_container): 178 """Working containers should be able to find the installed binaries, 179 scripts,etc even with restricted access""" 180 c = named_container.run( 181 tty=True, 182 detach=True, 183 read_only=True, 184 tmpfs = {'/run':'', '/tmp':''}, 185 ) 186 # Check that each program is available (on the PATH and executable) 187 for prog in PROG_LIST_TO_CHECK: 188 res = c.exec_run(["which", prog]) 189 assert res.output.decode('utf-8').rstrip().endswith(prog) 190 191 192