Skip to content

Commit 08601c7

Browse files
author
alanbchristie
authored
Merge pull request #8 from InformaticsMatters/7
Pull docker execution changes
2 parents 773ecdd + b2bed34 commit 08601c7

File tree

10 files changed

+1080
-771
lines changed

10 files changed

+1080
-771
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ work
1212
.nextflow.log*
1313
/tmp
1414
**/*.egg-info
15+
**/.DS_Store

README.md

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ installed as normal:
3232
released when more invasive tests have been written. In the meantime we
3333
use this repository as a Git [submodule] in our existing pipelines.
3434

35+
### Redirecting output
36+
Normally pipeline output files are written to a `tmp` directory inside
37+
the working copy of the repository you're running in. Alternatively you
38+
can write test output to your own directory (i.e. `/tmp/blob`) using
39+
the environment variable `POUT`: -
40+
41+
$ export POUT=/tmp/blob/
42+
43+
Output files are removed when the test suite starts and when it passes.
44+
3545
### From within a pipeline repository
3646
You will find this repository located as a submodule. When checking the
3747
pipeline out (for example [Pipelines]) you will need to initialise the
@@ -51,25 +61,57 @@ When tests fail it logs as much as it can and continues. When all the tests
5161
have finished it prints a summary including a lit of failed tests along with
5262
of the number of test files and individual tests that were executed: -
5363

54-
-------
55-
Summary
56-
-------
57-
Test Files : 20
58-
Tests : 30
59-
Tests ignored: 0
60-
Tests failed : 0
61-
-------
62-
Passed: TRUE
63-
64+
+----------------+
65+
: Summary:
66+
+----------------+
67+
: Test files: 29
68+
: Tests found: 39
69+
: Tests passed: 20
70+
: Tests failed: -
71+
: Tests skipped: 19
72+
: Tests ignored: 3
73+
: Warnings: -
74+
+----------------+
75+
: Result: SUCCESS
76+
+----------------+
77+
78+
Fields in the summary should be self-explanatory but a couple might benefit
79+
from further explanation: -
80+
81+
- `Tests skipped` are the tests that were found but not executed.
82+
Normally these are the tests found during a _Container_ test
83+
that can't be run (because the test has no corresponding container image).
84+
- `Tests ignored` are test found that are not run because they have
85+
been marked for non-execution as the test name begins with `ignore_`.
86+
6487
### From here
6588
If you have working copies of all your pipeline repositories checked-out
6689
in the same directory as this repository you can execute all the tests
6790
across all the repositories by running the tester from here. Simply
6891
run the following Gradle command from here: -
6992

7093
$ ./gradlew runPipelineTester
94+
95+
### In Docker
96+
You can run the pipeline tests in Docker using their expected container
97+
image (defined in the service descriptor). Doing this gives you added
98+
confidence that your pipeline will work wen deployed.
99+
100+
You can use the docker-specific Gradle task: -
101+
102+
$ ./gradlew runDockerPipelineTester
71103

72-
### Debugging test failures
104+
Or, by adding the `-d` or `--indocker` command-line argument into the basic
105+
task. To pass command-line options through Gradle into the underlying task
106+
you can also run the Docker tests like this: -
107+
108+
$ ./gradlew runPipelineTester -Pptargs=-d
109+
110+
> When you run _in docker_ only the tests that can run in Docker (those with
111+
a defined image name) will be executed. Tests that cannot be executed in
112+
Docker will be _skipped_.
113+
114+
## Debugging test failures
73115
Ideally your tests will pass. When they don't the test framework prints
74116
the collected log to the screen as it happens but also keeps all the files
75117
generated (by all the tests) in case they are of use for diagnostics.
@@ -80,6 +122,11 @@ for every file and test combination. For example, if the test file
80122
`pbf_ev.test` contains the test `test_pbf_ev_raw` any files it generates
81123
will be found in the directory `pbf_ev-test_pbf_ev_raw`.
82124

125+
You can re-direct the test output to an existing directory of your choice by
126+
defining the Environment variable `POUT`: -
127+
128+
$ export POUT=/tmp/my-output-dir/
129+
83130
Some important notes: -
84131

85132
- Files generated by the pipelines are removed when the tester is
@@ -102,6 +149,13 @@ in order to create a set of tests for a new pipeline.
102149
> At the moment the tester only permits one test file per pipeline so all
103150
tests for a given pipeline need to be composed in one file.
104151

152+
## Testing the pipeline utilities
153+
The pipeline utilities consist of a number of Python-based modules
154+
that can be tested using `setup.py`. To test these modules run the
155+
following from the `src/python` directory: -
156+
157+
$ python setup.py test
158+
105159
---
106160

107161
[Conda]: https://conda.io/docs/

build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,12 @@ task runPipelineTester(type: Exec) {
3434
commandLine 'groovy', 'PipelineTester.groovy', "$args"
3535

3636
}
37+
38+
task runDockerPipelineTester(type: Exec) {
39+
40+
description 'Runs the PipelineTester Docker tests'
41+
42+
workingDir 'src/groovy'
43+
commandLine 'groovy', 'PipelineTester.groovy', '-indocker'
44+
45+
}

pipeline.test.template

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@
6060
//
6161
// If a test is not working an you want to keep it and avoid running it
6262
// then simply prefix the test section name with `ignore_`.
63+
//
64+
// The location of any input data you provide in your test is normally
65+
// located in the project's `data` directory or mounted as `/data`
66+
// when running in a container. You can safely refer to data in both cases
67+
// with the PIN environment variable created by the PipelineTester.
68+
// To allow execution from the command-line and in a container the input
69+
// data file `blob.dat` should be referred to using `${PIN}blob.dat`
6370

6471
test_1 = [
6572

@@ -71,7 +78,7 @@
7178
// If you provide a command you **cannot** provide parameters
7279
// (see the params section below).
7380

74-
command: ```python my_own_command
81+
command: ```python my_own_command -i ${PIN}blob.dat
7582
--my-own-param-1 32
7683
--my-own-param-2 18.5```,
7784

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env groovy
2+
3+
/**
4+
* Copyright (c) 2018 Informatics Matters Ltd.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* The PipelineTester Container Executor class. The class is responsible for
21+
* executing a pipeline command in the supplied Docker container image.
22+
*/
23+
class ContainerExecutor {
24+
25+
/**
26+
* Executes the given command in the supplied container image. The data
27+
* directory (pin) is mounted as `/data` in the running container
28+
* and the designated output directory (pout) is mounted in the container
29+
* as `/output`. Two environment variables are defined: PIN and POUT and
30+
* are set to `/data` and `/output` respectively. The script is executed in
31+
* the output directory in the container.
32+
*
33+
* @param command The command to run
34+
* @param imageName The image to run the command in
35+
* @param pin The pipeline input directory
36+
* (used to define the PIN environment variable)
37+
* @param pout The pipeline output directory
38+
* (used to define the POUT environment variable)
39+
* @param timeoutSeconds The time to allow for the command to execute
40+
* @return A list containing the STDOUT and STDERR encapsulated in a
41+
* StringBuilder(), an integer command exit code and a timeout
42+
* boolean (set if the program execution timed out)
43+
*/
44+
static execute(String command, String imageName,
45+
String pin, String pout,
46+
int timeoutSeconds) {
47+
48+
StringBuilder sout = new StringBuilder()
49+
StringBuilder serr = new StringBuilder()
50+
51+
// Note: PIN and POUT have trailing forward-slashed for now
52+
// to allow a migratory use of $PIN}file references
53+
// rather than insisting on ${PIN}/file which would fail if
54+
// PIN wasn't defined - it's about lowest risk changes.
55+
56+
String cmd = "docker run -v $pin:/data -v $pout:/output" +
57+
" -w /output -e PIN=/data/ -e POUT=/output/ $imageName" +
58+
" sh -c '$command'"
59+
60+
def proc = ['sh', '-c', cmd].execute(null, new File('.'))
61+
proc.consumeProcessOutput(sout, serr)
62+
proc.waitForOrKill((long)timeoutSeconds * 1000)
63+
int exitValue = proc.exitValue()
64+
65+
// Timeout?
66+
//
67+
// Some exit codes have a special meaning.
68+
//
69+
// We can, for example, assume that the process was killed
70+
// if the exit code is 143 as 143, which is 128 + 15, means
71+
// the program died with signal 15 (SIGTERM).
72+
// See http://www.tldp.org/LDP/abs/html/exitcodes.html.
73+
boolean timeout = exitValue == 143 ? true : false
74+
75+
return [sout, serr, exitValue, timeout]
76+
77+
}
78+
79+
}

0 commit comments

Comments
 (0)