Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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')
Exemplo n.º 4
0
    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)
Exemplo n.º 5
0
    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")
Exemplo n.º 6
0
    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')
Exemplo n.º 7
0
 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')
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
    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))
Exemplo n.º 10
0
    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)
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    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""")
Exemplo n.º 14
0
    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, [])
Exemplo n.º 15
0
    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, [])
Exemplo n.º 16
0
    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)
Exemplo n.º 17
0
    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"""
            )
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
    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()
Exemplo n.º 20
0
 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)
Exemplo n.º 21
0
 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)
Exemplo n.º 22
0
 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__)
Exemplo n.º 23
0
    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))
Exemplo n.º 24
0
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)
Exemplo n.º 25
0
    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()
Exemplo n.º 26
0
    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()
Exemplo n.º 27
0
    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)