Skip to content

Commit 28be9c8

Browse files
author
Alan Christie
committed
Adds timeout defitnions
Adds nextflow config
1 parent c3c0634 commit 28be9c8

File tree

3 files changed

+100
-34
lines changed

3 files changed

+100
-34
lines changed

README.rst

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,25 @@ The Job Tester (``jote``) is used to run unit tests located in
99
Data Manager Job implementation repositories against the Job's
1010
container image.
1111

12-
Job implementations are required to provide a Manifest file (``manifest.yaml``)
13-
that lists Job Definition files (in the Job repository's ``data-manager``
14-
directory). The Manifest names at least ine file and the Job Definition
15-
should define at least one test for every Job. ``jote`` runs the tests
16-
but also ensures the repository structure meets the Data Manager requirements.
12+
Job implementations are required to provide at least one manifest file
13+
that lists Job Definition files (these reside in the ``data-manager``
14+
of the repository being tested). The default manifest file is
15+
``manifest.yaml`` but you can name your own and have more than one.
1716

18-
Tests are defined in the Job definition file. Here's a snippet illustrating a
17+
Each Manifest must name at least one file and the corresponding
18+
Job Definition file should define at least one test for every Job.
19+
``jote`` runs the tests but also ensures the repository structure is as
20+
expected and applies strict formatting of the YAML files.
21+
22+
Here's a snippet from a Job definition file illustrating a
1923
Job (``max-min-picker``) with a test called ``simple-execution``.
2024

2125
The test defines an input option (a file) and some other command options.
22-
The ``checks`` section defines the exit criteria of a successful test.
26+
The ``checks`` section is used to define the exit criteria of the test.
2327
In this case the container must exit with code ``0`` and the file
2428
``diverse.smi`` must be found (in the mounted project directory), i.e
25-
it must *exist* and contain ``100`` lines::
29+
it must *exist* and contain ``100`` lines. ``jote`` will ensure that these
30+
expectations are satisfied::
2631

2732
jobs:
2833
[...]
@@ -43,6 +48,21 @@ it must *exist* and contain ``100`` lines::
4348
- exists: true
4449
- lineCount: 100
4550

51+
Running tests
52+
-------------
53+
54+
Run ``jote`` from the root of a clone of the Data Manager Job implementation
55+
repository that you want to test::
56+
57+
jote
58+
59+
You can display the utility's help with::
60+
61+
jote --help
62+
63+
Ignoring tests
64+
--------------
65+
4666
Individual tests can be prevented from being executed by adding an `ignore`
4767
declaration::
4868

@@ -55,6 +75,8 @@ declaration::
5575
ignore:
5676
[...]
5777

78+
Test run levels
79+
---------------
5880
Tests can be assigned a ``run-level``. Run-levels are numerical value (1..100)
5981
that can be used to classify your tests, often using it to represent
6082
execution time. By default all tests that have no run-level and those with
@@ -73,10 +95,57 @@ You define the run-level in the root block of the job specification::
7395
run-level: 5
7496
[...]
7597

98+
Test timeouts
99+
-------------
100+
101+
``jote`` lets each test run for 10 minutes before cancelling (and failing) them.
102+
If you expect your test to run for more than 10 minutes you can use the
103+
``timeout-minutes`` property to define your own test-specific value::
104+
105+
jobs:
106+
[...]
107+
max-min-picker:
108+
[...]
109+
tests:
110+
simple-execution:
111+
timeout-minutes: 120
112+
[...]
113+
114+
Remember that job tests are *unit tests*, so long-running tests should be
115+
discouraged.
116+
117+
Nextflow execution
118+
------------------
119+
Job image type can be ``simple`` or ``nextflow``. Simple jobs are executed in
120+
the container image you've built and should behave much the same as they do
121+
when run within the Data Manager. Nextflow jobs on the other hand are executed
122+
using the shell, relying on Docker as the execution run-time for the processes
123+
in your workflow.
124+
125+
be aware that nextflow tests run by ``jote`` run under different conditions
126+
compared to those running under the Data Manager's control, where nextflow
127+
will be executed within a Kubernetes environment rather than docker. This
128+
introduces variability. Tests that run under ``jote`` *are not* running in the
129+
same environment or under the same memory or CPU constraints. Remember this
130+
when testing - i.e. a successful nextflow test under ``jote`` does not
131+
necessarily mean that it will execute successfully within the Data Manager.
132+
Successful nextflow tests run with ``jote`` are just and indication that
133+
the same execution might work in the Data Manager.
134+
135+
It's your responsibility to provide a suitable nextflow for shell execution,
136+
which ``jote`` simply uses when executing the test's ``command`` that's
137+
defined in your Job definition.
138+
139+
When running nextflow jobs ``jote`` writes a ``nextflow.config`` to the
140+
test's simulated project directory prior to executing the command.
141+
``jote`` *will not* let you have a nextflow config in your home directory
142+
as any settings found there would be merged with the file ``jote`` writes,
143+
potentially disturbing the execution behanviour.
144+
76145
Installation
77-
------------
146+
============
78147

79-
Pyconf is published on `PyPI`_ and can be installed from
148+
``jote```` is published on `PyPI`_ and can be installed from
80149
there::
81150

82151
pip install im-jote
@@ -89,18 +158,6 @@ To use the utility you will need to have installed `Docker`_.
89158
.. _PyPI: https://pypi.org/project/im-jote/
90159
.. _Docker: https://docs.docker.com/get-docker/
91160

92-
Running tests
93-
-------------
94-
95-
Run ``jote`` from the root of a clone of the Data Manager Job implementation
96-
repository that you want to test::
97-
98-
jote
99-
100-
You can display the utility's help with::
101-
102-
jote --help
103-
104161
Get in touch
105162
------------
106163

src/jote/compose.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
INSTANCE_DIRECTORY: str = '.instance-88888888-8888-8888-8888-888888888888'
1313

14-
# A default, 30 minute timeout
15-
DEFAULT_TEST_TIMEOUT_S: int = 30 * 60
14+
# A default test execution timeout
15+
DEFAULT_TEST_TIMEOUT_M: int = 10
1616

1717
# The user id containers will be started as
1818
_USER_ID: int = 8888
@@ -175,7 +175,8 @@ def create(self) -> str:
175175

176176
return project_path
177177

178-
def run(self) -> Tuple[int, str, str]:
178+
def run(self, timeout_minutes: int = DEFAULT_TEST_TIMEOUT_M)\
179+
-> Tuple[int, str, str]:
179180
"""Runs the container for the test, expecting the docker-compose file
180181
written by the 'create()'. The container exit code is returned to the
181182
caller along with the stdout and stderr content.
@@ -187,19 +188,18 @@ def run(self) -> Tuple[int, str, str]:
187188
cwd = os.getcwd()
188189
os.chdir(self.get_test_path())
189190

190-
timeout_s: int = DEFAULT_TEST_TIMEOUT_S
191191
try:
192192
# Run the container
193193
# and then cleanup
194194
test = subprocess.run(['docker-compose', 'up',
195195
'--exit-code-from', 'job',
196196
'--abort-on-container-exit'],
197197
capture_output=True,
198-
timeout=timeout_s,
198+
timeout=timeout_minutes * 60,
199199
check=False)
200200
_ = subprocess.run(['docker-compose', 'down'],
201201
capture_output=True,
202-
timeout=120,
202+
timeout=240,
203203
check=False)
204204
finally:
205205
os.chdir(cwd)

src/jote/jote.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Informatics Matters Job Tester (JOTE).
22
3-
get help running the utility with 'jote --help'
3+
Get help running this utility with 'jote --help'
44
"""
55
import argparse
66
import os
@@ -16,7 +16,7 @@
1616

1717
from decoder import decoder
1818

19-
from .compose import get_test_root, INSTANCE_DIRECTORY, DEFAULT_TEST_TIMEOUT_S
19+
from .compose import get_test_root, INSTANCE_DIRECTORY, DEFAULT_TEST_TIMEOUT_M
2020
from .compose import Compose
2121

2222
# Where can we expect to find Job definitions?
@@ -301,7 +301,9 @@ def _check(t_compose: Compose,
301301
return True
302302

303303

304-
def _run_nextflow(command: str, project_path: str)\
304+
def _run_nextflow(command: str,
305+
project_path: str,
306+
timeout_minutes: int = DEFAULT_TEST_TIMEOUT_M)\
305307
-> Tuple[int, str, str]:
306308
"""Runs nextflow in the project directory returning the exit code,
307309
stdout and stderr.
@@ -326,7 +328,7 @@ def _run_nextflow(command: str, project_path: str)\
326328
try:
327329
test = subprocess.run(command.split(),
328330
capture_output=True,
329-
timeout=DEFAULT_TEST_TIMEOUT_S,
331+
timeout=timeout_minutes * 60,
330332
check=False)
331333
finally:
332334
os.chdir(cwd)
@@ -538,18 +540,25 @@ def _test(args: argparse.Namespace,
538540
# Run the container
539541
if test_status and not args.dry_run:
540542

543+
timeout_minutes: Optional[int] = None
544+
if 'timeout-minutes' in job_definition.tests[job_test_name]:
545+
timeout_minutes = job_definition. \
546+
tests[job_test_name]['timeout-minutes']
547+
541548
exit_code: int = 0
542549
out: str = ''
543550
err: str = ''
544551
if job_image_type in [_IMAGE_TYPE_SIMPLE]:
545552
# Run the image container
546553
assert t_compose
547-
exit_code, out, err = t_compose.run()
554+
exit_code, out, err = t_compose.run(timeout_minutes)
548555
elif job_image_type in [_IMAGE_TYPE_NEXTFLOW]:
549556
# Run nextflow directly
550557
assert job_command
551558
assert project_path
552-
exit_code, out, err = _run_nextflow(job_command, project_path)
559+
exit_code, out, err = _run_nextflow(job_command,
560+
project_path,
561+
timeout_minutes)
553562
else:
554563
print('! FAILURE')
555564
print(f'! unsupported image-type ({job_image_type}')

0 commit comments

Comments
 (0)