Skip to content

Commit

Permalink
cli: new flag for overriding workflow parameters
Browse files Browse the repository at this point in the history
* Introducing new flag for `start` command to override workflow
  input parameters. Closes #200

Signed-off-by: Rokas Maciulaitis <rokas.maciulaitis@cern.ch>
  • Loading branch information
Rokas Maciulaitis committed Nov 19, 2018
1 parent d5dc320 commit 6743830
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 14 deletions.
15 changes: 15 additions & 0 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ so that a hard copy of the link target is uploaded to the cloud
storage workspace. The link is not maintained throughout the
workflow execution.

Overriding default input parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to run a workflow with different input parameters than the ones in
``reana.yaml``, you can do it by running `reana-client start` with flag ``-p``
and specifying parameters that you want to override.

Note that parameters passed with ``-p`` flag must exist in reana.yaml.
Non-existing parameters will be skipped.

.. code-block:: console
$ reana-client start -p myparam1=myval1 -p myparam2=myval2
workflow.1 has been started.
Downloading outputs
~~~~~~~~~~~~~~~~~~~

Expand Down
29 changes: 28 additions & 1 deletion reana_client/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Client(BaseAPIClient):
def ping(self):
"""Health check REANA server."""
try:
response, http_response = self._client.api.get_api_ping().result()
response, http_response = self._client.api.ping().result()
if http_response.status_code == 200:
return response['message']
else:
Expand Down Expand Up @@ -363,3 +363,30 @@ def upload_to_server(self, workflow, paths, access_token):
logging.debug(str(e))
logging.info("Something went wrong while uploading {}".
format(fname))

def get_workflow_parameters(self, workflow, access_token):
"""Get parameters of previously created workflow."""
try:
response, http_response = self.\
_client.api.get_workflow_parameters(
workflow_id_or_name=workflow,
access_token=access_token)\
.result()
if http_response.status_code == 200:
return response
else:
raise Exception(
"Expected status code 200 but replied with "
"{status_code}".format(
status_code=http_response.status_code))

except HTTPError as e:
logging.debug(
'Workflow parameters could not be retrieved: '
'\nStatus: {}\nReason: {}\n'
'Message: {}'.format(e.response.status_code,
e.response.reason,
e.response.json()['message']))
raise Exception(e.response.json()['message'])
except Exception as e:
raise e
57 changes: 47 additions & 10 deletions reana_client/cli/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
from reana_client.cli.utils import add_access_token_options
from reana_client.cli.files import upload_files

from reana_client.cli.files import upload_files

from reana_db.database import Session
from reana_db.models import Workflow


class _WorkflowStatus(Enum):
created = 0
Expand Down Expand Up @@ -196,10 +201,11 @@ def workflow_create(ctx, file, name, skip_validation, access_token):
help="""
Start previously created workflow.
The workflow execution can be further influenced by setting operational
prameters using `-p` or `--parameter` option. The option can be
repetitive. For example, to disable caching for the Serial workflow
engine, you can set ``-p CACHE=off``.
The workflow execution can be further influenced by setting input prameters
using `-p` or `--parameters` flag or by setting operational options using
`-o` or `--options`. The input parameters and operational options can be
repetitive. For example, to disable caching for the Serial workflow engine,
you can set ``-o CACHE=off``.
""")
@click.option(
'-w',
Expand All @@ -210,14 +216,22 @@ def workflow_create(ctx, file, name, skip_validation, access_token):
'Overrides value of REANA_WORKON.')
@add_access_token_options
@click.option(
'-p', '--parameter',
'-p', '--parameter', 'parameters',
multiple=True,
help='Optional operational parameters for the workflow execution. '
help='Additional input parameters to override '
'original ones from reana.yaml. '
'E.g. -p myparam1=myval1 -p myparam2=myval2.',
)
@click.option(
'-o', '--option', 'options',
multiple=True,
help='Additional operatioal options for the workflow execution. '
'E.g. CACHE=off.',
)
@click.pass_context
@with_api_client
def workflow_start(ctx, workflow, access_token, parameter): # noqa: D301
def workflow_start(ctx, workflow, access_token,
parameters, options): # noqa: D301
"""Start previously created workflow."""
logging.debug('command: {}'.format(ctx.command_path.replace(" ", ".")))
for p in ctx.params:
Expand All @@ -229,10 +243,33 @@ def workflow_start(ctx, workflow, access_token, parameter): # noqa: D301
fg='red'), err=True)
sys.exit(1)

parsed_parameters = {'parameters':
dict(p.split('=') for p in parameter)}
parsed_parameters = {'input_parameters':
dict(p.split('=') for p in parameters)}
parsed_parameters['operational_options'] = \
dict(p.split('=') for p in options)

if workflow:
if parameters:
try:
response = \
ctx.obj.client.get_workflow_parameters(workflow,
access_token)
parsed_input_parameters = \
dict(parsed_parameters['input_parameters'])
for parameter in parsed_input_parameters.keys():
if parameter not in response['parameters']:
click.echo(
click.style('Given parameter - {0}, is not in '
'reana.yaml'.format(parameter),
fg='red'),
err=True)
del parsed_parameters['input_parameters'][parameter]
except Exception as e:
click.echo(
click.style('Could not apply given input parameters: '
'{0} \n{1}'.format(parameters, str(e))),
err=True)

try:
logging.info('Connecting to {0}'.format(ctx.obj.client.server_url))
response = ctx.obj.client.start_workflow(workflow,
Expand Down Expand Up @@ -510,7 +547,7 @@ def workflow_validate(ctx, file):
@click.option(
'-p', '--parameter',
multiple=True,
help='Optional operational parameters for the workflow execution. '
help='Optional operational options for the workflow execution. '
'E.g. CACHE=off.',
)
@add_access_token_options
Expand Down
2 changes: 1 addition & 1 deletion reana_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def load_reana_spec(filepath, skip_validation=False):
get('specification')
kwargs['parameters'] = \
reana_yaml.get('inputs', {}).get('parameters', {})

kwargs['original'] = True
reana_yaml['workflow']['specification'] = load_workflow_spec(
reana_yaml['workflow']['type'],
reana_yaml['workflow'].get('file'),
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
'pytest-cache>=1.0',
'pytest-cov>=1.8.0',
'pytest-pep8>=1.0.6',
'pytest-reana>=0.4.0,<0.5.0',
'pytest-reana>=v0.5.0.dev20181119'
]

extras_require = {
Expand All @@ -54,7 +54,7 @@
'click>=7,<8',
'cwltool==1.0.20180912090223',
'pyOpenSSL==17.5.0', # FIXME remove once yadage-schemas solves deps.
'reana-commons>=0.4.0,<0.5.0',
'reana-commons>=0.5.0.dev20181116,<0.6.0',
'rfc3987==1.3.7', # FIXME remove once yadage-schemas solves deps.
'strict-rfc3339==0.7', # FIXME remove once yadage-schemas solves deps.
'tablib>=0.12.1,<0.13',
Expand Down
26 changes: 26 additions & 0 deletions tests/test_cli_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,29 @@ def test_run(workflow_start_mock,
assert workflow_create_mock.called is True
assert upload_file_mock.called is True
assert workflow_start_mock.called is True


def test_workflow_input_parameters(mock_base_api_client):
"""Test if not existing input parameters from CLI are applied."""
status_code = 200
response = {'id': 'd9304bdf-0d19-45d9-ae87-d5fd18059193',
'name': 'workflow.19',
'parameters': {'helloworld': 'code/helloworld.py',
'inputfile': 'data/names.txt',
'outputfile': 'results/greetings.txt',
'sleeptime': 2}}
env = {'REANA_SERVER_URL': 'localhost'}
mocked_api_client = mock_base_api_client(status_code,
response,
'reana-server')
parameter = "Debug"
expected_message = '{0}, is not in reana.yaml'.format(parameter)
config = Config(mocked_api_client)
reana_token = '000000'
runner = CliRunner(env=env)
result = runner.invoke(
cli,
['start', '-at', reana_token, '-w workflow.19',
'-p {0}=True'.format(parameter)],
obj=config)
assert expected_message in result.output

0 comments on commit 6743830

Please sign in to comment.