diff --git a/.github/workflows/docker_wrapper.yml b/.github/workflows/docker_wrapper.yml new file mode 100644 index 0000000..48cb7b4 --- /dev/null +++ b/.github/workflows/docker_wrapper.yml @@ -0,0 +1,29 @@ +name: Python application + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 2 + uses: actions/setup-python@v2 + with: + python-version: 2.7 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 mock + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Run tests + run: | + docker_wrapper/tests.py diff --git a/docker.py b/docker_wrapper/docker.py similarity index 82% rename from docker.py rename to docker_wrapper/docker.py index 72a53b4..b112a9b 100755 --- a/docker.py +++ b/docker_wrapper/docker.py @@ -6,11 +6,11 @@ from socket import getfqdn -def gateway(): +def gateway(argv): gateway_needed = False - if any('atl' in arg for arg in sys.argv) or \ - any('cms' in arg for arg in sys.argv) or \ - any('lhcb' in arg for arg in sys.argv): + if any('atl' in arg for arg in argv) or \ + any('cms' in arg for arg in argv) or \ + any('lhcb' in arg for arg in argv): gateway_needed = True return gateway_needed and not os.path.isfile('/etc/nogateway') @@ -52,7 +52,7 @@ def args_create(argv): # Prevent any privilege escalation (prevents setuid programs from running) #dargs.append('--security-opt no-new-privileges') - if gateway(): + if gateway(argv): dargs.append('--label=xrootd-local-gateway=true') dargs.append('--network=ralworker') dargs.append('--add-host=xrootd.echo.stfc.ac.uk ceph-gw10.gridpp.rl.ac.uk ceph-gw11.gridpp.rl.ac.uk:172.28.1.1') @@ -103,21 +103,18 @@ def execute(args): exit(p.returncode) -# ============================================================================== -# main -# ============================================================================== +if __name__ == '__main__': + docker_command = sys.argv[1] + dargs = ['/usr/bin/sudo', '/usr/bin/docker', docker_command] -docker_command = sys.argv[1] -dargs = ['/usr/bin/sudo', '/usr/bin/docker', docker_command] - -if docker_command == 'create': - dargs += args_create(sys.argv[2:]) -elif docker_command == 'run': - dargs += args_run(sys.argv[2:]) -else: - dargs += args_other_commands(sys.argv[2:]) + if docker_command == 'create': + dargs += args_create(sys.argv[2:]) + elif docker_command == 'run': + dargs += args_run(sys.argv[2:]) + else: + dargs += args_other_commands(sys.argv[2:]) -if os.environ.get('DOCKER_WRAPPER_DEBUG'): - dargs = ['/bin/echo'] + dargs + if os.environ.get('DOCKER_WRAPPER_DEBUG'): + dargs = ['/bin/echo'] + dargs -execute(dargs) + execute(dargs) diff --git a/package_docker.sh b/docker_wrapper/package.sh similarity index 100% rename from package_docker.sh rename to docker_wrapper/package.sh diff --git a/docker_wrapper/test_resources/atlas_job.txt b/docker_wrapper/test_resources/atlas_job.txt new file mode 100644 index 0000000..18b8f5a --- /dev/null +++ b/docker_wrapper/test_resources/atlas_job.txt @@ -0,0 +1,2 @@ +run --cpu-shares=100 --memory=2000m --hostname tatls002-4104905.0-lcg2156.gridpp.rl.ac.uk --name HTCJob4104905_0_slot1_30_PID27383 -e TEMP=/pool/condor/dir_27383 -e _CONDOR_SCRATCH_DIR=/pool/condor/dir_27383 -e _CONDOR_SLOT=slot1_30 -e BATCH_SYSTEM=HTCondor -e TMPDIR=/pool/condor/dir_27383 -e _CONDOR_CHIRP_CONFIG=/pool/condor/dir_27383/.chirp.config -e _CONDOR_JOB_PIDS= -e TMP=/pool/condor/dir_27383 -e OMP_NUM_THREADS=1 -e _CONDOR_JOB_AD=/pool/condor/dir_27383/.job.ad -e _CONDOR_JOB_IWD=/pool/condor/dir_27383 -e _CHIRP_DELAYED_UPDATE_PREFIX=Chirp -e _CONDOR_MACHINE_AD=/pool/condor/dir_27383/.machine.ad -e X509_USER_PROXY=/pool/condor/dir_27383/user.proxy --volume /pool/condor/dir_27383:/pool/condor/dir_27383 --volume /etc/grid-security:/etc/grid-security:ro --volume /etc/machinefeatures:/etc/machinefeatures:ro --volume /etc/profile.d/grid-env.sh:/etc/profile.d/grid-env.sh:ro --volume /etc/glexec.conf:/etc/glexec.conf:ro --volume /etc/lcmaps:/etc/lcmaps:ro --volume /etc/lcas:/etc/lcas:ro --volume /cvmfs:/cvmfs:shared --volume /etc/cvmfs:/etc/cvmfs:ro --volume /sys/fs/cgroup:/sys/fs/cgroup:ro --volume /pool/atlas/recovery:/pool/atlas/recovery --volume /etc/atlas:/etc/atlas:ro --volume /etc/arc:/etc/arc:ro --workdir /pool/condor/dir_27383 --user 61857:35514 stfc/grid-workernode-c7:2019-07-02.1 ./condor_exec.exe +run -eSINGULARITY_BINDPATH=/etc/hosts --cap-add=SYS_ADMIN --cap-add=DAC_OVERRIDE --cap-add=SETUID --cap-add=SETGID --cap-add=SYS_CHROOT --env=SINGULARITYENV_FRONTIER_LOG_FILE=frontier.log --cpu-shares=100 --memory=4000m --memory-reservation=2000m --network=ralworker --add-host=xrootd.echo.stfc.ac.uk ceph-gw10.gridpp.rl.ac.uk ceph-gw11.gridpp.rl.ac.uk:172.28.1.1 --label=xrootd-local-gateway=true --env=XrdSecGSISRVNAMES=lcg2156.gridpp.rl.ac.uk --env=SINGULARITYENV_XrdSecGSISRVNAMES=lcg2156.gridpp.rl.ac.uk --env=PANDA_HOSTNAME=lcg2156.gridpp.rl.ac.uk --env=SINGULARITYENV_PANDA_HOSTNAME=lcg2156.gridpp.rl.ac.uk --env=PILOT_NOKILL=1 --env=FRONTIER_LOG_FILE=frontier.log --hostname tatls002-4104905.0-lcg2156.gridpp.rl.ac.uk --name HTCJob4104905_0_slot1_30_PID27383 -e TEMP=/pool/condor/dir_27383 -e _CONDOR_SCRATCH_DIR=/pool/condor/dir_27383 -e _CONDOR_SLOT=slot1_30 -e BATCH_SYSTEM=HTCondor -e TMPDIR=/pool/condor/dir_27383 -e _CONDOR_CHIRP_CONFIG=/pool/condor/dir_27383/.chirp.config -e _CONDOR_JOB_PIDS= -e TMP=/pool/condor/dir_27383 -e OMP_NUM_THREADS=1 -e _CONDOR_JOB_AD=/pool/condor/dir_27383/.job.ad -e _CONDOR_JOB_IWD=/pool/condor/dir_27383 -e _CHIRP_DELAYED_UPDATE_PREFIX=Chirp -e _CONDOR_MACHINE_AD=/pool/condor/dir_27383/.machine.ad -e X509_USER_PROXY=/pool/condor/dir_27383/user.proxy --volume /pool/condor/dir_27383:/pool/condor/dir_27383 --volume /etc/grid-security:/etc/grid-security:ro --volume /etc/machinefeatures:/etc/machinefeatures:ro --volume /etc/profile.d/grid-env.sh:/etc/profile.d/grid-env.sh:ro --volume /etc/glexec.conf:/etc/glexec.conf:ro --volume /etc/lcmaps:/etc/lcmaps:ro --volume /etc/lcas:/etc/lcas:ro --volume /cvmfs:/cvmfs:shared --volume /etc/cvmfs:/etc/cvmfs:ro --volume /sys/fs/cgroup:/sys/fs/cgroup:ro --volume /pool/atlas/recovery:/pool/atlas/recovery --volume /etc/atlas:/etc/atlas:ro --volume /etc/arc:/etc/arc:ro --workdir /pool/condor/dir_27383 --user 61857:35514 stfc/grid-workernode-c7:2019-07-02.1 nice -n 10 ./condor_exec.exe diff --git a/docker_wrapper/test_resources/cms_job.txt b/docker_wrapper/test_resources/cms_job.txt new file mode 100644 index 0000000..17720a3 --- /dev/null +++ b/docker_wrapper/test_resources/cms_job.txt @@ -0,0 +1,2 @@ +run --cpu-shares=800 --memory=24600m --hostname ttcms032-3870779.0-lcg1928.gridpp.rl.ac.uk --name HTCJob3870779_0_slot1_1_PID2668 -e TEMP=/pool/condor/dir_2668 -e _CONDOR_SCRATCH_DIR=/pool/condor/dir_2668 -e _CONDOR_SLOT=slot1_1 -e BATCH_SYSTEM=HTCondor -e TMPDIR=/pool/condor/dir_2668 -e _CONDOR_CHIRP_CONFIG=/pool/condor/dir_2668/.chirp.config -e _CONDOR_JOB_PIDS= -e TMP=/pool/condor/dir_2668 -e OMP_NUM_THREADS=8 -e _CONDOR_JOB_AD=/pool/condor/dir_2668/.job.ad -e _CONDOR_JOB_IWD=/pool/condor/dir_2668 -e _CHIRP_DELAYED_UPDATE_PREFIX=Chirp -e _CONDOR_MACHINE_AD=/pool/condor/dir_2668/.machine.ad -e X509_USER_PROXY=/pool/condor/dir_2668/user.proxy --volume /pool/condor/dir_2668:/pool/condor/dir_2668 --volume /etc/grid-security:/etc/grid-security:ro --volume /etc/machinefeatures:/etc/machinefeatures:ro --volume /etc/profile.d/grid-env.sh:/etc/profile.d/grid-env.sh:ro --volume /etc/glexec.conf:/etc/glexec.conf:ro --volume /etc/lcmaps:/etc/lcmaps:ro --volume /etc/lcas:/etc/lcas:ro --volume /cvmfs:/cvmfs:shared --volume /etc/cvmfs:/etc/cvmfs:ro --volume /etc/arc:/etc/arc:ro --workdir /pool/condor/dir_2668 --user 63099:35515 stfc/grid-workernode-c7:2019-07-02.1 ./condor_exec.exe +run -eSINGULARITY_BINDPATH=/etc/hosts --cap-add=SYS_ADMIN --cap-add=DAC_OVERRIDE --cap-add=SETUID --cap-add=SETGID --cap-add=SYS_CHROOT --env=SINGULARITYENV_FRONTIER_LOG_FILE=frontier.log --cpu-shares=800 --memory=49200m --memory-reservation=24600m --network=ralworker --add-host=xrootd.echo.stfc.ac.uk ceph-gw10.gridpp.rl.ac.uk ceph-gw11.gridpp.rl.ac.uk:172.28.1.1 --label=xrootd-local-gateway=true --env=XrdSecGSISRVNAMES=lcg1928.gridpp.rl.ac.uk --env=SINGULARITYENV_XrdSecGSISRVNAMES=lcg1928.gridpp.rl.ac.uk --env=PANDA_HOSTNAME=lcg1928.gridpp.rl.ac.uk --env=SINGULARITYENV_PANDA_HOSTNAME=lcg1928.gridpp.rl.ac.uk --env=PILOT_NOKILL=1 --env=FRONTIER_LOG_FILE=frontier.log --hostname ttcms032-3870779.0-lcg1928.gridpp.rl.ac.uk --name HTCJob3870779_0_slot1_1_PID2668 -e TEMP=/pool/condor/dir_2668 -e _CONDOR_SCRATCH_DIR=/pool/condor/dir_2668 -e _CONDOR_SLOT=slot1_1 -e BATCH_SYSTEM=HTCondor -e TMPDIR=/pool/condor/dir_2668 -e _CONDOR_CHIRP_CONFIG=/pool/condor/dir_2668/.chirp.config -e _CONDOR_JOB_PIDS= -e TMP=/pool/condor/dir_2668 -e OMP_NUM_THREADS=8 -e _CONDOR_JOB_AD=/pool/condor/dir_2668/.job.ad -e _CONDOR_JOB_IWD=/pool/condor/dir_2668 -e _CHIRP_DELAYED_UPDATE_PREFIX=Chirp -e _CONDOR_MACHINE_AD=/pool/condor/dir_2668/.machine.ad -e X509_USER_PROXY=/pool/condor/dir_2668/user.proxy --volume /pool/condor/dir_2668:/pool/condor/dir_2668 --volume /etc/grid-security:/etc/grid-security:ro --volume /etc/machinefeatures:/etc/machinefeatures:ro --volume /etc/profile.d/grid-env.sh:/etc/profile.d/grid-env.sh:ro --volume /etc/glexec.conf:/etc/glexec.conf:ro --volume /etc/lcmaps:/etc/lcmaps:ro --volume /etc/lcas:/etc/lcas:ro --volume /cvmfs:/cvmfs:shared --volume /etc/cvmfs:/etc/cvmfs:ro --volume /etc/arc:/etc/arc:ro --workdir /pool/condor/dir_2668 --user 63099:35515 stfc/grid-workernode-c7:2019-07-02.1 nice -n 10 ./condor_exec.exe diff --git a/docker_wrapper/test_resources/cms_job.txt! b/docker_wrapper/test_resources/cms_job.txt! new file mode 100644 index 0000000..3950490 --- /dev/null +++ b/docker_wrapper/test_resources/cms_job.txt! @@ -0,0 +1 @@ + run --cpu-shares=800 --memory=24600m --hostname ttcms032-3870779.0-lcg1928.gridpp.rl.ac.uk --name HTCJob3870779_0_slot1_1_PID2668 -e TEMP=/pool/condor/dir_2668 -e _CONDOR_SCRATCH_DIR=/pool/condor/dir_2668 -e _CONDOR_SLOT=slot1_1 -e BATCH_SYSTEM=HTCondor -e TMPDIR=/pool/condor/dir_2668 -e _CONDOR_CHIRP_CONFIG=/pool/condor/dir_2668/.chirp.config -e _CONDOR_JOB_PIDS= -e TMP=/pool/condor/dir_2668 -e OMP_NUM_THREADS=8 -e _CONDOR_JOB_AD=/pool/condor/dir_2668/.job.ad -e _CONDOR_JOB_IWD=/pool/condor/dir_2668 -e _CHIRP_DELAYED_UPDATE_PREFIX=Chirp -e _CONDOR_MACHINE_AD=/pool/condor/dir_2668/.machine.ad -e X509_USER_PROXY=/pool/condor/dir_2668/user.proxy --volume /pool/condor/dir_2668:/pool/condor/dir_2668 --volume /etc/grid-security:/etc/grid-security:ro --volume /etc/machinefeatures:/etc/machinefeatures:ro --volume /etc/profile.d/grid-env.sh:/etc/profile.d/grid-env.sh:ro --volume /etc/glexec.conf:/etc/glexec.conf:ro --volume /etc/lcmaps:/etc/lcmaps:ro --volume /etc/lcas:/etc/lcas:ro --volume /cvmfs:/cvmfs:shared --volume /etc/cvmfs:/etc/cvmfs:ro --volume /etc/arc:/etc/arc:ro --workdir /pool/condor/dir_2668 --user 63099:35515 stfc/grid-workernode-c7:2019-07-02.1 ./condor_exec.exe' diff --git a/docker_wrapper/tests.py b/docker_wrapper/tests.py new file mode 100755 index 0000000..d2ebda1 --- /dev/null +++ b/docker_wrapper/tests.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# coding=utf8 + +import unittest + +try: + from unittest.mock import patch +except ImportError: + from mock import patch + +from random import choice, randint +from string import ascii_letters +from os.path import dirname, join + +import docker + + +class TestDockerWrapper(unittest.TestCase): + def setUp(self): + self.maxDiff = None + self.resource_dir = join(dirname(__file__), 'test_resources') + self.atlas_job_args_before, self.atlas_job_args_after = [ + l.split() for l in open(join(self.resource_dir, 'atlas_job.txt')).readlines() + ] + self.cms_job_args_before, self.cms_job_args_after = [ + l.split() for l in open(join(self.resource_dir, 'cms_job.txt')).readlines() + ] + + def mocked_getfqdn(self): + return 'host.example.org' + + def test_gateway(self): + self.assertFalse(docker.gateway([])) + self.assertFalse(docker.gateway([''])) + self.assertFalse(docker.gateway(['version'])) + + self.assertTrue(docker.gateway(self.atlas_job_args_before)) + self.assertTrue(docker.gateway(self.cms_job_args_before)) + + def test_args_create(self): + basic_args = [ + '-eSINGULARITY_BINDPATH=/etc/hosts', + '--cap-add=SYS_ADMIN', + '--cap-add=DAC_OVERRIDE', + '--cap-add=SETUID', + '--cap-add=SETGID', + '--cap-add=SYS_CHROOT', + '--env=SINGULARITYENV_FRONTIER_LOG_FILE=frontier.log', + '--env=PANDA_HOSTNAME=host.example.org', + '--env=SINGULARITYENV_PANDA_HOSTNAME=host.example.org', + '--env=PILOT_NOKILL=1', + ] + with patch('docker.getfqdn') as mock_socket: + mock_socket.return_value = 'host.example.org' + + self.assertEqual(docker.args_create([]), basic_args + ['--label=xrootd-local-gateway=false']) + self.assertEqual(docker.args_create(['']), basic_args + ['--label=xrootd-local-gateway=false', '']) + + self.assertEqual(docker.args_create(self.atlas_job_args_before), self.atlas_job_args_after) + self.assertEqual(docker.args_create(self.cms_job_args_before), self.cms_job_args_after) + + def test_args_run(self): + with patch('docker.args_create') as mock_args_create: + test_args = ['foo', 'bar'] + docker.args_run(test_args) + mock_args_create.assert_called_with(test_args) + + def test_args_other_commands(self): + random_args = [ + ''.join([ choice(ascii_letters) for _ in range(0, randint(3, 9)) ]) + for __ in range(0, randint(2, 12)) + ] + # args_other_commands should return the input without modification + self.assertEqual(random_args, docker.args_other_commands(random_args)) + + +if __name__ == '__main__': + unittest.main()