Skip to content

Commit

Permalink
cli: new setup-environment and run-example commands
Browse files Browse the repository at this point in the history
* Adds two new convenient helper commands to set up the shell environment and to
  run selected REANA examples with selected workflow engines. Useful for local
  integration testing purposes.

Signed-off-by: Tibor Simko <tibor.simko@cern.ch>
  • Loading branch information
tiborsimko committed Aug 1, 2018
1 parent af98986 commit d9be467
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 17 deletions.
172 changes: 157 additions & 15 deletions reana/cli.py
Expand Up @@ -61,13 +61,11 @@
'reana.io',
]


REPO_LIST_CLIENT = [
'reana-commons',
'reana-client',
]


REPO_LIST_CLUSTER = [
'reana-commons',
'reana-job-controller',
Expand All @@ -81,12 +79,17 @@
'reana-workflow-monitor',
]


REPO_LIST_CLUSTER_CLI = [
'reana-commons',
'reana-cluster',
]

WORKFLOW_LIST_ALL = [
'cwl',
'serial',
'yadage'
]


@click.group()
def cli(): # noqa: D301
Expand Down Expand Up @@ -123,6 +126,15 @@ def cli(): # noqa: D301
$ eval "$(reana git-fork -c ALL)"
$ reana git-clone -c ALL
How to install latest ``master`` REANA cluster and client CLI scripts:
.. code-block:: console
\b
$ workon reana
$ reana install-client
$ reana install-cluster
How to compile and deploy latest ``master`` REANA cluster:
.. code-block:: console
Expand All @@ -134,23 +146,20 @@ def cli(): # noqa: D301
$ reana docker-images
$ pip install reana-cluster
$ reana-cluster -f reana-cluster-latest.yaml init
$ # we now have REANA cluster running "master" versions of components
How to install latest ``master`` REANA client CLI script:
How to set up your shell environment variables:
.. code-block:: console
\b
$ workon reana
$ reana install-client
$ eval $(reana setup-environment)
How to install latest ``master`` REANA cluster CLI script:
How to run full REANA example using a given workflow engine:
.. code-block:: console
\b
$ workon reana
$ reana install-cluster
$ reana run-example -c reana-demo-root6-roofit -w serial -s 10
How to test one component pull request:
Expand All @@ -161,8 +170,6 @@ def cli(): # noqa: D301
$ reana git-checkout -b . 72 --fetch
$ reana docker-build -c .
$ kubectl delete pod -l app=job-controller
$ kubectl get pods
$ # we can now try to run an example
How to test multiple component branches:
Expand All @@ -175,8 +182,6 @@ def cli(): # noqa: D301
$ reana docker-build
$ kubectl delete pod -l app=job-controller
$ kubectl delete pod -l app=workflow-controller
$ kubectl get pods
$ # we can now try to run an example
How to release and push cluster component images:
Expand All @@ -190,6 +195,7 @@ def cli(): # noqa: D301
$ reana docker-build -t 0.3.0.dev20180625
$ reana docker-push -t 0.3.0.dev20180625
$ # we should now make PR for ``reana-cluster.yaml`` to use given tag
"""
pass

Expand Down Expand Up @@ -321,6 +327,26 @@ def select_components(components):
return list(output)


def select_workflows(workflows):
"""Return good workflow values that REANA supports for running examples.
:param workflows: A list of workflow names such as 'cwl'.
:type components: list
:return: Unique workflow names.
:rtype: list
"""
output = set([])
for workflow in workflows:
if workflow in WORKFLOW_LIST_ALL:
output.add(workflow)
else:
display_message('Ignoring unknown workflow {0}.'.format(
workflow))
return list(output)


def is_component_dockerised(component):
"""Return whether the component contains Dockerfile.
Expand All @@ -339,6 +365,37 @@ def is_component_dockerised(component):
return False


def is_component_runnable_example(component):
"""Return whether the component contains reana.yaml.
Useful for safety check when using some run-example commands for those
components that are not REANA examples.
:param component: standard component name
:type component: str
:return: True/False whether the component is a REANA example
:rtype: bool
"""
if os.path.exists(get_srcdir(component) + os.sep + 'reana.yaml'):
return True
return False


def construct_workflow_name(example, workflow):
"""Construct suitable workflow name for given REANA example and workflow.
:param example: REANA example (e.g. reana-demo-root6-roofit)
:param workflow: workflow to use (cwl, serial, yadage)
:type example: str
:type workflow: str
"""
output = 'ci.{0}.{1}'.format(example.replace('reana-demo-', ''),
workflow)
return output


def run_command(cmd, component=''):
"""Run given command in the given component source directory.
Expand All @@ -355,7 +412,8 @@ def run_command(cmd, component=''):
try:
subprocess.run(cmd, shell=True, check=True)
except subprocess.CalledProcessError as err:
sys.exit(err.cmd)
click.secho('[{0}] {1}'.format(component, err), bold=True)
sys.exit(err.returncode)


def display_message(msg, component=''):
Expand Down Expand Up @@ -899,3 +957,87 @@ def install_cluster(): # noqa: D301
'pip install . --upgrade',
]:
run_command(cmd, component)


@cli.command(name='setup-environment')
def setup_environment(): # noqa: D301
"""Display commands to set up shell environment variables for local cluster.
Display commands how to set up REANA_SERVER_URL and REANA_ACCESS_TOKEN
suitable for current local REANA cluster deployment. The output should be
passed to eval.
"""
my_reana_server_url_cmd = subprocess.getoutput('reana-cluster env')
my_reana_access_token = subprocess.getoutput('kubectl logs -l app=server |'
' grep access_token: |'
' awk \'{print $NF;}\' |'
' tr -d \'[:space:]\'')
print(my_reana_server_url_cmd)
print('export REANA_ACCESS_TOKEN={0}'.format(my_reana_access_token))


@click.option('--component', '-c', multiple=True,
default=['reana-demo-root6-roofit'],
help='Which examples to run? [reana-demo-root6-roofit]')
@click.option('--workflow', '-w', multiple=True,
default=['cwl', 'serial', 'yadage'],
help='Which workflow engine to run? [cwl,serial,yadage]')
@click.option('--output', '-o', multiple=True,
default=['plot.png'],
help='Expected output file? [plot.png]')
@click.option('--sleep', '-s', default=60,
help='How much seconds to wait for results? [60]')
@cli.command(name='run-example')
def run_example(component, workflow, output, sleep): # noqa: D301
"""Run given REANA example with given workflow engine.
\b
:param component: The option ``component`` can be repeated. The value is
the repository name of the example.
[default=reana-demo-root6-roofit]
:param workflow: The option ``workflow`` can be repeated. The value is the
workflow engine to use to run the example.
[default=cwl,serial,yadage]
:param output: The option ``output`` can be repeated. The value is the
expected output file the workflow should produce.
[default=plot.png]
:param sleep: How much seconds to sleep in order to wait for workflow to be
finished before checking the results? [default=60]
:type component: str
:type workflow: str
:type sleep: int
"""
components = select_components(component)
workflows = select_workflows(workflow)
reana_yaml = {
'cwl': 'reana-cwl.yaml',
'serial': 'reana.yaml',
'yadage': 'reana-yadage.yaml',
}
for component in components:
for workflow in workflows:
workflow_name = construct_workflow_name(component, workflow)
# run example workflow:
for cmd in [
'reana-client create -f {0} -n {1}'.format(
reana_yaml[workflow], workflow_name),
'reana-client upload ./code ./inputs -w {0}'.format(
workflow_name),
'reana-client start -w {0}'.format(
workflow_name),
'sleep {0}'.format(sleep),
'reana-client status -w {0}'.format(
workflow_name),
'reana-client list -w {0}'.format(
workflow_name),
]:
run_command(cmd, component)
# verify output file presence:
for output_file in output:
cmd = 'reana-client list -w {0} | grep -q {1}'.format(
workflow_name, output_file)
run_command(cmd, component)
# report status; everything was OK
run_command('echo OK', component)
46 changes: 44 additions & 2 deletions tests/test_cli.py
Expand Up @@ -42,8 +42,20 @@ def test_is_component_dockerised():
"""Tests for is_component_dockerised()."""
from reana.cli import is_component_dockerised
if os.environ.get('REANA_SRCDIR'):
assert is_component_dockerised('reana-workflow-controller') is True
assert is_component_dockerised('reana-cluster') is False
assert is_component_dockerised(
'reana-workflow-controller') is True
assert is_component_dockerised(
'reana-cluster') is False


def test_is_component_runnable_example():
"""Tests for is_component_runnable_example()."""
from reana.cli import is_component_runnable_example
if os.environ.get('REANA_SRCDIR'):
assert is_component_runnable_example(
'reana-workflow-controller') is False
assert is_component_runnable_example(
'reana-demo-worldpopulation') is True


def test_select_components():
Expand Down Expand Up @@ -76,6 +88,24 @@ def test_select_components():
assert output_obtained.sort() == output_expected.sort()


def test_select_workflows():
"""Tests for select_workflows()."""
from reana.cli import select_workflows
for (input_value, output_expected) in (
# regular workflows:
(['cwl', ], ['cwl', ]),
(['serial', ], ['serial', ]),
(['cwl', 'yadage', ], ['cwl', 'yadage, ']),
# bad values:
(['nonsense', ], []),
(['nonsense', 'cwl', ], ['cwl', ]),
# output uniqueness:
(['cwl', 'cwl', ], ['cwl', ]),
):
output_obtained = select_workflows(input_value)
assert output_obtained.sort() == output_expected.sort()


def test_find_standard_component_name():
"""Tests for find_standard_component_name()."""
from reana.cli import find_standard_component_name
Expand All @@ -97,3 +127,15 @@ def test_uniqueness_of_short_names():
if short_name in short_names:
raise Exception('Found ')
short_names.append(short_name)


def test_construct_workflow_name():
"""Tests for construct_workflow_name()."""
from reana.cli import construct_workflow_name
for (input_value, output_expected) in (
(('reana', 'cwl'), 'ci.reana.cwl'),
(('reana-demo-root6-roofit', 'yadage'), 'ci.root6-roofit.yadage'),
):
output_obtained = construct_workflow_name(input_value[0],
input_value[1])
assert output_obtained == output_expected

0 comments on commit d9be467

Please sign in to comment.