def test_clone_repos(self): wf_data = {"steps": [{"uses": "popperized/bin/sh@master"}]} wf = WorkflowParser.parse(wf_data=wf_data) cache_dir = os.path.join(os.environ["HOME"], ".cache/popper/") # clone repos in the default cache directory. conf = ConfigLoader.load() runner = WorkflowRunner(conf) runner._clone_repos(wf) step_dir = os.path.join(cache_dir, conf.wid, "github.com/popperized/bin") self.assertTrue(os.path.exists(step_dir)) # clone repos in custom cache directory os.environ["POPPER_CACHE_DIR"] = "/tmp/smdir" conf = ConfigLoader.load() runner = WorkflowRunner(conf) runner._clone_repos(wf) step_dir = os.path.join("/tmp/smdir", conf.wid, "github.com/popperized/bin") self.assertTrue(os.path.exists(step_dir)) os.environ.pop("POPPER_CACHE_DIR") # check failure when container is not available and we skip cloning shutil.rmtree("/tmp/smdir") shutil.rmtree(cache_dir) conf = ConfigLoader.load(skip_clone=True) runner = WorkflowRunner(conf) self.assertRaises(SystemExit, runner._clone_repos, wf)
def test_docker_basic_run(self): repo = self.mk_repo() conf = ConfigLoader.load(workspace_dir=repo.working_dir) test_string = "STEP_INFO:popper:Successfully tagged popperized/bin:master" with WorkflowRunner(conf) as r: wf_data = { "steps": [{ "uses": "popperized/bin/sh@master", "args": ["ls"], }] } with self.assertLogs(log, level="STEP_INFO") as cm: r.run(WorkflowParser.parse(wf_data=wf_data)) self.assertTrue(test_string in cm.output, f"Got cmd output: {cm.output}") wf_data = { "steps": [{ "uses": "docker://*****:*****@master", "args": ["ls"], }] } with self.assertLogs(log, level="STEP_INFO") as cm: r.run(WorkflowParser.parse(wf_data=wf_data)) self.assertTrue(test_string not in cm.output) repo.close() shutil.rmtree(repo.working_dir, ignore_errors=True)
def test_setup_base_cache(self): cache_dir = WorkflowRunner._setup_base_cache() try: self.assertEqual(cache_dir, os.environ['XDG_CACHE_HOME']) except KeyError: self.assertEqual(cache_dir, os.path.join(os.environ['HOME'], '.cache/popper')) os.environ['POPPER_CACHE_DIR'] = '/tmp/popper' cache_dir = WorkflowRunner._setup_base_cache() self.assertEqual(cache_dir, '/tmp/popper') os.environ.pop('POPPER_CACHE_DIR')
def test_clone_repos(self): wf = YMLWorkflow(""" version: '1' steps: - uses: popperized/bin/sh@master """) wf.parse() conf = PopperConfig() cache_dir = os.path.join(os.environ['HOME'], '.cache/popper/') # clone repos in the default cache directory. runner = WorkflowRunner(conf) runner._clone_repos(wf) step_dir = os.path.join(cache_dir, conf.wid, 'github.com/popperized/bin') self.assertTrue(os.path.exists(step_dir)) # clone repos in custom cache directory os.environ['POPPER_CACHE_DIR'] = '/tmp/smdir' runner._clone_repos(wf) step_dir = os.path.join('/tmp/smdir', conf.wid, 'github.com/popperized/bin') self.assertTrue(os.path.exists(step_dir)) os.environ.pop('POPPER_CACHE_DIR') # check failure when container is not available and we skip cloning shutil.rmtree('/tmp/smdir') shutil.rmtree(cache_dir) conf = PopperConfig(skip_clone=True) runner = WorkflowRunner(conf) self.assertRaises(SystemExit, runner._clone_repos, wf)
def test_check_secrets(self): wf_data = { "steps": [{ "uses": "docker://alpine:3.9", "args": ["ls", "-ltr"], "secrets": ["SECRET_ONE", "SECRET_TWO"], }] } wf = WorkflowParser.parse(wf_data=wf_data) # in dry-run, secrets are ignored runner = WorkflowRunner(ConfigLoader.load(dry_run=True)) runner._process_secrets(wf) # now go back to not dry-running runner = WorkflowRunner(ConfigLoader.load()) # when CI=true it should fail os.environ["CI"] = "true" self.assertRaises(SystemExit, runner._process_secrets, wf) # add one secret os.environ["SECRET_ONE"] = "1234" # it should fail again, as we're missing one self.assertRaises(SystemExit, runner._process_secrets, wf) os.environ.pop("CI") # now is fine with patch("getpass.getpass", return_value="5678"): runner._process_secrets(wf) # pop the other os.environ.pop("SECRET_ONE")
def test_check_secrets(self): wf = YMLWorkflow(""" version: '1' steps: - uses: docker://alpine:3.9 args: ["ls -ltr"] secrets: ["SECRET_ONE", "SECRET_TWO"] """) wf.parse() # in dry-run, secrets are ignored runner = WorkflowRunner(PopperConfig(dry_run=True)) runner._process_secrets(wf) # now go back to not dry-running runner = WorkflowRunner(PopperConfig()) # when CI=true it should fail os.environ['CI'] = 'true' self.assertRaises(SystemExit, runner._process_secrets, wf) # add one secret os.environ['SECRET_ONE'] = '1234' # it should fail again, as we're missing one self.assertRaises(SystemExit, runner._process_secrets, wf) os.environ.pop('CI') # now is fine with patch('getpass.getpass', return_value='5678'): runner._process_secrets(wf) # pop the other os.environ.pop('SECRET_ONE')
def test_steprunner_factory(self): with WorkflowRunner(PopperConfig()) as r: self.assertEqual( r._step_runner('host', None).__class__.__name__, 'HostRunner') self.assertEqual( r._step_runner('docker', None).__class__.__name__, 'DockerRunner')
def cli(ctx, file, step, entrypoint, skip_pull, skip_clone, substitution, conf): """Opens an interactive shell using all the attributes defined in the workflow file for the given STEP, but ignoring ``runs`` and ``args``. By default, it invokes /bin/bash. If you need to invoke another one, you can specify it in the --entrypoint flag. NOTE: this command only works for (local) host runner in Docker. """ wf = WorkflowParser.parse(file=file, step=step, immutable=False, substitutions=substitution) # override entrypoint step = wf.steps[0] step.args = [] step.runs = entrypoint # configure runner so containers execute in attached mode and create a tty config = ConfigLoader.load( engine_name="docker", pty=True, skip_pull=skip_pull, skip_clone=skip_clone, config_file=conf, ) with WorkflowRunner(config) as runner: try: runner.run(wf) except Exception as e: log.debug(traceback.format_exc()) log.fail(e)
def test_exec_mpi_failure(self, mock_kill): config_dict = { "engine": { "name": "singularity", "options": {}, }, "resource_manager": { "name": "slurm", "options": { "1": { "nodes": 2, "nodelist": "worker1,worker2", "overcommit": True } }, }, } config = ConfigLoader.load(workspace_dir="/w", config_file=config_dict) self.Popen.set_command( "sbatch " "--wait --overcommit " f"popper_1_{config.wid}.sh", returncode=12, ) self.Popen.set_command(f"tail -f popper_1_{config.wid}.out", returncode=0) with WorkflowRunner(config) as r: wf_data = {"steps": [{"uses": "docker://alpine", "args": ["ls"]}]} self.assertRaises(SystemExit, r.run, WorkflowParser.parse(wf_data=wf_data))
def test_docker_basic_run(self): repo = self.mk_repo() conf = ConfigLoader.load(workspace_dir=repo.working_dir) with WorkflowRunner(conf) as r: wf_data = {"steps": [{"uses": "popperized/bin/sh@master", "args": ["ls"],}]} r.run(WorkflowParser.parse(wf_data=wf_data)) wf_data = { "steps": [ { "uses": "docker://alpine:3.9", "args": ["sh", "-c", "echo $FOO > hello.txt ; pwd"], "env": {"FOO": "bar"}, } ] } r.run(WorkflowParser.parse(wf_data=wf_data)) with open(os.path.join(repo.working_dir, "hello.txt"), "r") as f: self.assertEqual(f.read(), "bar\n") wf_data = { "steps": [ { "uses": "docker://alpine:3.9", "args": ["nocommandisnamedlikethis"], } ] } self.assertRaises(SystemExit, r.run, WorkflowParser.parse(wf_data=wf_data)) repo.close() shutil.rmtree(repo.working_dir, ignore_errors=True)
def test_submit_job_failure(self, mock_kill): self.Popen.set_command( 'sbatch --wait --job-name popper_1_123abc ' '--output /tmp/popper/slurm/popper_1_123abc.out ' '/tmp/popper/slurm/popper_1_123abc.sh', returncode=12) self.Popen.set_command('tail -f /tmp/popper/slurm/popper_1_123abc.out', returncode=0) config_dict = { 'engine': { 'name': 'docker', 'options': {} }, 'resource_manager': { 'name': 'slurm', 'options': {} } } config = PopperConfig(workspace_dir='/w', config_file=config_dict) config.wid = "123abc" with WorkflowRunner(config) as r: wf = YMLWorkflow(""" version: '1' steps: - uses: 'popperized/bin/sh@master' runs: [cat] args: README.md """) wf.parse() self.assertRaises(SystemExit, r.run, wf) call_tail = call.Popen( ['tail', '-f', '/tmp/popper/slurm/popper_1_123abc.out'], cwd=os.getcwd(), env=None, preexec_fn=os.setsid, stderr=-2, stdout=-1, universal_newlines=True) call_sbatch = call.Popen([ 'sbatch', '--wait', '--job-name', 'popper_1_123abc', '--output', '/tmp/popper/slurm/popper_1_123abc.out', '/tmp/popper/slurm/popper_1_123abc.sh' ], cwd=os.getcwd(), env=None, preexec_fn=os.setsid, stderr=-2, stdout=-1, universal_newlines=True) self.assertEqual(call_tail in self.Popen.all_calls, True) self.assertEqual(call_sbatch in self.Popen.all_calls, True)
def test_run(self, mock_kill): config_dict = { "engine": { "name": "podman", "options": { "privileged": True, "hostname": "popper.local", "domainname": "www.example.org", "volumes": ["/path/in/host:/path/in/container"], "environment": { "FOO": "bar" }, }, }, "resource_manager": { "name": "slurm" }, } config = ConfigLoader.load(workspace_dir="/w", config_file=config_dict) self.Popen.set_command( f"sbatch --wait --job-name popper_1_{config.wid} " f"--output {slurm_cache_dir}/popper_1_{config.wid}.out " f"{slurm_cache_dir}/popper_1_{config.wid}.sh", returncode=0, ) self.Popen.set_command( f"tail -f {slurm_cache_dir}/popper_1_{config.wid}.out", returncode=0) with WorkflowRunner(config) as r: wf_data = { "steps": [{ "uses": "popperized/bin/sh@master", "runs": ["cat"], "args": ["README.md"], }] } r.run(WorkflowParser.parse(wf_data=wf_data)) with open(f"{slurm_cache_dir}/popper_1_{config.wid}.sh", "r") as f: # fmt: off expected = f"""#!/bin/bash podman rm -f popper_1_{config.wid} || true podman build -t popperized/bin:master {os.environ['HOME']}/.cache/popper/{config.wid}/github.com/popperized/bin/sh podman create --name popper_1_{config.wid} --workdir /workspace --entrypoint cat -v /w:/workspace:Z -v /path/in/host:/path/in/container -e FOO=bar --privileged --hostname popper.local --domainname www.example.org popperized/bin:master README.md podman start --attach popper_1_{config.wid}""" # fmt: on actual = f.read() self.maxDiff = None self.assertEqual(expected, actual)
def test_run(self, mock_kill): self.Popen.set_command( 'sbatch --wait --job-name popper_1_123abc ' '--output /tmp/popper/slurm/popper_1_123abc.out ' '/tmp/popper/slurm/popper_1_123abc.sh', returncode=0) self.Popen.set_command('tail -f /tmp/popper/slurm/popper_1_123abc.out', returncode=0) config_dict = { 'engine': { 'name': 'docker', 'options': { 'privileged': True, 'hostname': 'popper.local', 'domainname': 'www.example.org', 'volumes': ['/path/in/host:/path/in/container'], 'environment': { 'FOO': 'bar' } } }, 'resource_manager': { 'name': 'slurm' } } config = PopperConfig(workspace_dir='/w', config_file=config_dict) config.wid = "123abc" with WorkflowRunner(config) as r: wf = YMLWorkflow(""" version: '1' steps: - uses: 'popperized/bin/sh@master' runs: [cat] args: README.md """) wf.parse() r.run(wf) with open('/tmp/popper/slurm/popper_1_123abc.sh', 'r') as f: content = f.read() self.assertEqual( content, f"""#!/bin/bash docker rm -f popper_1_123abc || true docker build -t popperized/bin:master {os.environ['HOME']}/.cache/popper/123abc/github.com/popperized/bin/sh docker create --name popper_1_123abc --workdir /workspace --entrypoint cat -v /w:/workspace -v /var/run/docker.sock:/var/run/docker.sock -v /path/in/host:/path/in/container -e FOO=bar --privileged --hostname popper.local --domainname www.example.org popperized/bin:master README.md docker start --attach popper_1_123abc""")
def test_dry_run(self): config = ConfigLoader.load(engine_name="singularity", resman_name="slurm", dry_run=True) with WorkflowRunner(config) as r: wf_data = { "steps": [{ "uses": "docker://alpine", "runs": ["cat"], "args": ["README.md"], }] } r.run(WorkflowParser.parse(wf_data=wf_data)) self.assertEqual(self.Popen.all_calls, [])
def test_dry_run(self): repo = self.mk_repo() config = PopperConfig(engine_name='docker', resman_name='slurm', dry_run=True, workspace_dir=repo.working_dir) with WorkflowRunner(config) as r: wf = YMLWorkflow(""" version: '1' steps: - uses: 'popperized/bin/sh@master' runs: [cat] args: README.md """) wf.parse() r.run(wf) self.assertEqual(self.Popen.all_calls, [])
def test_slurm_singularity_run(self, mock_kill): config_dict = { "engine": { "name": "singularity", "options": { "hostname": "popper.local", "bind": ["/path/in/host:/path/in/container"], }, }, "resource_manager": { "name": "slurm" }, } config = ConfigLoader.load(workspace_dir="/w", config_file=config_dict) # fmt: off self.Popen.set_command( f"sbatch --wait --job-name popper_1_{config.wid} --output {slurm_cache_dir}/popper_1_{config.wid}.out {slurm_cache_dir}/popper_1_{config.wid}.sh", returncode=0, ) # fmt: on self.Popen.set_command( f"tail -f {slurm_cache_dir}/popper_1_{config.wid}.out", returncode=0) with WorkflowRunner(config) as r: wf_data = { "steps": [{ "uses": "popperized/bin/sh@master", "args": ["ls"], }] } r.run(WorkflowParser.parse(wf_data=wf_data)) with open(f"{slurm_cache_dir}/popper_1_{config.wid}.sh", "r") as f: # fmt: off expected = f"""#!/bin/bash singularity run --userns --pwd /workspace --bind /w:/workspace --bind /path/in/host:/path/in/container --hostname popper.local {os.environ['HOME']}/.cache/popper/singularity/{config.wid}/popper_1_{config.wid}.sif ls""" # fmt: on actual = f.read() self.assertEqual(expected, actual)
def test_run(self, mock_kill): self.Popen.set_command( 'sbatch --wait --job-name popper_1_123abc ' '--output /tmp/popper/slurm/popper_1_123abc.out ' '/tmp/popper/slurm/popper_1_123abc.sh', returncode=0) self.Popen.set_command('tail -f /tmp/popper/slurm/popper_1_123abc.out', returncode=0) config_dict = { 'engine': { 'name': 'singularity', 'options': { 'hostname': 'popper.local', 'bind': ['/path/in/host:/path/in/container'] } }, 'resource_manager': { 'name': 'slurm' } } config = PopperConfig(workspace_dir='/w', config_file=config_dict) config.wid = "123abc" with WorkflowRunner(config) as r: wf = YMLWorkflow(""" version: '1' steps: - uses: 'popperized/bin/sh@master' runs: ls """) wf.parse() r.run(wf) with open('/tmp/popper/slurm/popper_1_123abc.sh', 'r') as f: content = f.read() self.assertEqual( content, f"""#!/bin/bash singularity exec --userns --pwd /workspace --bind /w:/workspace --bind /path/in/host:/path/in/container --hostname popper.local {os.environ['HOME']}/.cache/popper/singularity/123abc/popper_1_123abc.sif ls""" )
def test_host_run(self): repo = self.mk_repo() conf = ConfigLoader.load(workspace_dir=repo.working_dir) with WorkflowRunner(conf) as r: wf_data = { "steps": [{"uses": "sh", "runs": ["cat"], "args": ["README.md"],}] } r.run(WorkflowParser.parse(wf_data=wf_data)) wf_data = { "steps": [ { "uses": "sh", "runs": ["bash", "-c", "echo $FOO > hello.txt ; pwd"], "env": {"FOO": "bar"}, } ] } r.run(WorkflowParser.parse(wf_data=wf_data)) with open(os.path.join(repo.working_dir, "hello.txt"), "r") as f: self.assertEqual(f.read(), "bar\n") wf_data = {"steps": [{"uses": "sh", "runs": ["nocommandisnamedlikethis"]}]} self.assertRaises(SystemExit, r.run, WorkflowParser.parse(wf_data=wf_data)) # check exit code 78 wf_data = { "steps": [ {"uses": "sh", "runs": ["touch", "one.txt"]}, {"uses": "sh", "runs": ["bash", "-c", "exit 78"]}, {"uses": "sh", "runs": ["touch", "two.txt"]}, ] } r.run(WorkflowParser.parse(wf_data=wf_data)) self.assertTrue(os.path.isfile(os.path.join(repo.working_dir, "one.txt"))) self.assertFalse(os.path.isfile(os.path.join(repo.working_dir, "two.txt"))) repo.close() shutil.rmtree(repo.working_dir, ignore_errors=True)
def test_run(self): repo = self.mk_repo() conf = PopperConfig(workspace_dir=repo.working_dir) with WorkflowRunner(conf) as r: wf = YMLWorkflow(""" version: '1' steps: - uses: sh runs: [cat] args: README.md """) wf.parse() r.run(wf) wf = YMLWorkflow(""" version: '1' steps: - uses: 'sh' runs: ['bash', '-c', 'echo $FOO > hello.txt ; pwd'] env: { FOO: bar } """) wf.parse() r.run(wf) with open(os.path.join(repo.working_dir, 'hello.txt'), 'r') as f: self.assertEqual(f.read(), 'bar\n') wf = YMLWorkflow(""" version: '1' steps: - uses: 'sh' runs: 'nocommandisnamedlikethis' """) wf.parse() self.assertRaises(SystemExit, r.run, wf) repo.close()
def test_build_and_push_image(self): repo = self.mk_repo() config = { "resource_manager": { "name": "kubernetes", "options": { "registry_user": "******" }, } } kwargs = {"config_file": config} conf = ConfigLoader.load(**kwargs) step = Box( { "uses": "docker://alpine:3.9", "args": ["ls"], "name": "one", }, default_box=True, ) with WorkflowRunner(conf) as wf: with DockerRunner(config=conf) as dr: # this would not do anything just skip function dr._build_and_push_image(step)
def _setup_singularity_cache(self): self._singularity_cache = os.path.join( WorkflowRunner._setup_base_cache(), 'singularity', self._config.wid) if not os.path.exists(self._singularity_cache): os.makedirs(self._singularity_cache, exist_ok=True)
def test_steprunner_factory(self): with WorkflowRunner(ConfigLoader.load()) as r: self.assertEqual("HostRunner", r._step_runner("host", None).__class__.__name__) self.assertEqual("DockerRunner", r._step_runner("docker", None).__class__.__name__)
def test_run(self, mock_kill): self.maxDiff = None config_dict = { "engine": { "name": "singularity", "options": { "hostname": "popper.local", "bind": ["/path/in/host:/path/in/container"], }, }, "resource_manager": { "name": "slurm", "options": { "1": { "nodes": 2, "ntasks": 2, "nodelist": "worker1,worker2" } }, }, } config = ConfigLoader.load(workspace_dir="/w", config_file=config_dict) # fmt: off self.Popen.set_command( "sbatch " "--wait " f"popper_1_{config.wid}.sh", returncode=0, ) # fmt: on self.Popen.set_command(f"tail -f popper_1_{config.wid}.out", returncode=0) with WorkflowRunner(config) as r: wf_data = {"steps": [{"uses": "docker://alpine", "args": ["ls"]}]} r.run(WorkflowParser.parse(wf_data=wf_data)) with open(f"popper_1_{config.wid}.sh", "r") as f: # fmt: off expected = f"""#!/bin/bash #SBATCH --job-name=popper_1_{config.wid} #SBATCH --output=popper_1_{config.wid}.out #SBATCH --nodes=2 #SBATCH --ntasks=2 #SBATCH --ntasks-per-node=1 #SBATCH --nodelist=worker1,worker2 mpirun singularity run --userns --pwd /workspace --bind /w:/workspace --bind /path/in/host:/path/in/container --hostname popper.local {os.environ['HOME']}/.cache/popper/singularity/{config.wid}/popper_1_{config.wid}.sif ls""" # fmt: on actual = f.read() self.assertEqual(expected, actual) config_dict = { "engine": { "name": "singularity", "options": { "hostname": "popper.local", "bind": ["/path/in/host:/path/in/container"], }, }, "resource_manager": { "name": "slurm", "options": { "1": { "mpi": False, "nodes": 2, "ntasks": 2, "nodelist": "worker1,worker2", } }, }, } config = ConfigLoader.load(workspace_dir="/w", config_file=config_dict) self.Popen.set_command( f"srun --nodes 2 --ntasks 2 --ntasks-per-node 1 --nodelist worker1,worker2 rm -rf popper_1_{config.wid}.sif", returncode=0, ) self.Popen.set_command( f"srun --nodes 2 --ntasks 2 --ntasks-per-node 1 --nodelist worker1,worker2 singularity pull docker://alpine:latest", returncode=0, ) self.Popen.set_command( f"srun --nodes 2 --ntasks 2 --ntasks-per-node 1 --nodelist worker1,worker2 singularity run --userns --pwd /workspace --bind /w:/workspace --bind /path/in/host:/path/in/container --hostname popper.local {os.environ['HOME']}/.cache/popper/singularity/{config.wid}/popper_1_{config.wid}.sif ls", returncode=0, ) with WorkflowRunner(config) as r: wf_data = {"steps": [{"uses": "docker://alpine", "args": ["ls"]}]} r.run(WorkflowParser.parse(wf_data=wf_data))
def cli( ctx, step, wfile, debug, dry_run, log_file, quiet, reuse, engine, resource_manager, skip, skip_pull, skip_clone, substitution, allow_loose, workspace, conf, ): """Runs a Popper workflow. Only executes STEP if given. To specify a container engine to use other than docker, use the --engine/-e flag. For executing on a resource manager such as SLURM or Kubernetes, use the --resource-manager/-r flag. Alternatively, a configuration file can be given (--conf flag) that can specify container options, resource manager options, or both (see "Workflow Syntax and Execution Runtime" section of the Popper documentation for more). If the container engine (-e) or resource manager (-r) are specified with a flag and a configuration file is given as well, the values passed via the flags are given preference over those contained in the configuration file. """ # set the logging levels. level = "STEP_INFO" if quiet: level = "INFO" if debug: level = "DEBUG" log.setLevel(level) if dry_run: logging.msg_prefix = "DRYRUN: " if log_file: # also log to a file logging.add_log(log, log_file) # check conflicting flags and fail if needed if skip and step: log.fail("`--skip` can not be used when STEP argument is passed.") # invoke wf factory; handles formats, validations, filtering wf = WorkflowParser.parse( wfile, step=step, skipped_steps=skip, substitutions=substitution, allow_loose=allow_loose, ) config = ConfigLoader.load( engine_name=engine, resman_name=resource_manager, config_file=conf, reuse=reuse, dry_run=dry_run, skip_pull=skip_pull, skip_clone=skip_clone, workspace_dir=workspace, ) with WorkflowRunner(config) as runner: try: runner.run(wf) except Exception as e: log.debug(traceback.format_exc()) log.fail(e)
def test_singularity_start(self): repo = self.mk_repo() conf = PopperConfig(engine_name='singularity', workspace_dir=repo.working_dir) step = { 'uses': 'docker://*****:*****@master' args: 'ls' """) wf.parse() r.run(wf) wf = YMLWorkflow(""" version: '1' steps: - uses: 'docker://alpine:3.9' runs: ['sh', '-c', 'echo $FOO > hello.txt ; pwd'] env: { FOO: bar } """) wf.parse() r.run(wf) with open(os.path.join(repo.working_dir, 'hello.txt'), 'r') as f: self.assertEqual(f.read(), 'bar\n') wf = YMLWorkflow(""" version: '1' steps: - uses: 'docker://alpine:3.9' runs: 'nocommandisnamedlikethis' """) wf.parse() self.assertRaises(SystemExit, r.run, wf) repo.close()
def test_singularity_start(self): repo = self.mk_repo() conf = ConfigLoader.load(engine_name="singularity", workspace_dir=repo.working_dir) step = Box( { "uses": "docker://*****:*****@master", "args": ["ls"], }] } r.run(WorkflowParser.parse(wf_data=wf_data)) wf_data = { "steps": [{ "uses": "docker://alpine:3.9", "args": ["sh", "-c", "echo $FOO > hello.txt ; pwd"], "env": { "FOO": "bar" }, }] } r.run(WorkflowParser.parse(wf_data=wf_data)) with open(os.path.join(repo.working_dir, "hello.txt"), "r") as f: self.assertEqual(f.read(), "bar\n") wf_data = { "steps": [{ "uses": "docker://alpine:3.9", "args": ["nocommandisnamedlikethis"], }] } self.assertRaises(SystemExit, r.run, WorkflowParser.parse(wf_data=wf_data)) repo.close()
def test_submit_job_failure(self, mock_kill): config_dict = { "engine": { "name": "docker", "options": {} }, "resource_manager": { "name": "slurm", "options": {} }, } config = ConfigLoader.load(workspace_dir="/w", config_file=config_dict) self.Popen.set_command( f"sbatch --wait --job-name popper_1_{config.wid} " f"--output {slurm_cache_dir}/popper_1_{config.wid}.out " f"{slurm_cache_dir}/popper_1_{config.wid}.sh", returncode=12, ) self.Popen.set_command( f"tail -f {slurm_cache_dir}/popper_1_{config.wid}.out", returncode=0) with WorkflowRunner(config) as r: wf_data = { "steps": [{ "uses": "popperized/bin/sh@master", "runs": ["cat"], "args": ["README.md"], }] } self.assertRaises(SystemExit, r.run, WorkflowParser.parse(wf_data=wf_data)) call_tail = call.Popen( ["tail", "-f", f"{slurm_cache_dir}/popper_1_{config.wid}.out"], cwd=os.getcwd(), env=None, preexec_fn=os.setsid, stderr=-2, stdout=-1, universal_newlines=True, ) call_sbatch = call.Popen( [ "sbatch", "--wait", "--job-name", f"popper_1_{config.wid}", "--output", f"{slurm_cache_dir}/popper_1_{config.wid}.out", f"{slurm_cache_dir}/popper_1_{config.wid}.sh", ], cwd=os.getcwd(), env=None, preexec_fn=os.setsid, stderr=-2, stdout=-1, universal_newlines=True, ) self.assertEqual(call_tail in self.Popen.all_calls, True) self.assertEqual(call_sbatch in self.Popen.all_calls, True)