11package org .labkey .api .sequenceanalysis .run ;
22
3- import org .apache .commons .io .FileUtils ;
43import org .apache .commons .lang3 .StringUtils ;
54import org .apache .logging .log4j .Logger ;
5+ import org .jetbrains .annotations .Nullable ;
66import org .labkey .api .pipeline .PipelineJobException ;
77import org .labkey .api .sequenceanalysis .pipeline .PipelineContext ;
88import org .labkey .api .sequenceanalysis .pipeline .PipelineOutputTracker ;
1313import java .io .IOException ;
1414import java .io .PrintWriter ;
1515import java .util .Arrays ;
16+ import java .util .Collection ;
17+ import java .util .Collections ;
18+ import java .util .HashMap ;
19+ import java .util .HashSet ;
1620import java .util .List ;
21+ import java .util .Map ;
22+ import java .util .Set ;
23+ import java .util .stream .Collectors ;
1724
1825public class DockerWrapper extends AbstractCommandWrapper
1926{
2027 private final String _containerName ;
2128 private final PipelineContext _ctx ;
2229 private File _tmpDir = null ;
30+ private String _entryPoint = null ;
31+ private boolean _runPrune = true ;
32+ private String _alternateUserHome = null ;
33+ private final Map <String , String > _dockerEnvironment = new HashMap <>();
2334
2435 public DockerWrapper (String containerName , Logger log , PipelineContext ctx )
2536 {
@@ -28,12 +39,32 @@ public DockerWrapper(String containerName, Logger log, PipelineContext ctx)
2839 _ctx = ctx ;
2940 }
3041
42+ public void setAlternateUserHome (String alternateUserHome )
43+ {
44+ _alternateUserHome = alternateUserHome ;
45+ }
46+
3147 public void setTmpDir (File tmpDir )
3248 {
3349 _tmpDir = tmpDir ;
3450 }
3551
52+ public void setEntryPoint (String entryPoint )
53+ {
54+ _entryPoint = entryPoint ;
55+ }
56+
57+ public void setRunPrune (boolean runPrune )
58+ {
59+ _runPrune = runPrune ;
60+ }
61+
3662 public void executeWithDocker (List <String > containerArgs , File workDir , PipelineOutputTracker tracker ) throws PipelineJobException
63+ {
64+ executeWithDocker (containerArgs , workDir , tracker , null );
65+ }
66+
67+ public void executeWithDocker (List <String > containerArgs , File workDir , PipelineOutputTracker tracker , @ Nullable Collection <File > inputFiles ) throws PipelineJobException
3768 {
3869 File localBashScript = new File (workDir , "docker.sh" );
3970 File dockerBashScript = new File (workDir , "dockerRun.sh" );
@@ -45,70 +76,131 @@ public void executeWithDocker(List<String> containerArgs, File workDir, Pipeline
4576 {
4677 writer .println ("#!/bin/bash" );
4778 writer .println ("set -x" );
48- writer .println ("WD=`pwd` " );
49- writer . println ( "HOME=`echo ~/`" );
79+ writer .println ("set -e " );
80+
5081 writer .println ("DOCKER='" + SequencePipelineService .get ().getDockerCommand () + "'" );
51- writer .println ("sudo $DOCKER pull " + _containerName );
52- writer .println ("sudo $DOCKER run --rm=true \\ " );
53- writer .println ("\t -v \" ${WD}:/work\" \\ " );
54- writer .println ("\t -v \" ${HOME}:/homeDir\" \\ " );
55- _ctx .getDockerVolumes ().forEach (ln -> writer .println (ln + " \\ " ));
82+ writer .println ("$DOCKER pull " + _containerName );
83+ if (_runPrune )
84+ {
85+ writer .println ("$DOCKER image prune -f" );
86+ }
87+
88+ writer .println ("$DOCKER run --rm=true \\ " );
89+ writer .println ("\t --group-add keep-groups \\ " );
90+
91+ // NOTE: getDockerVolumes() should be refactored to remove the -v and this logic should be updated accordingly:
92+ File homeDir = new File (System .getProperty ("user.home" ));
93+ if (homeDir .exists ())
94+ {
95+ if (_ctx .getDockerVolumes ().stream ().noneMatch (homeDir .getPath ()::startsWith ))
96+ {
97+ writer .println ("\t -v '" + homeDir .getPath () + "':'" + homeDir .getPath () + "' \\ " );
98+ }
99+ else
100+ {
101+ _ctx .getLogger ().debug ("homeDir already present in docker volumes, will not re-add" );
102+ }
103+
104+ _dockerEnvironment .put ("USER_HOME" , homeDir .getPath ());
105+ }
106+
107+ if (_alternateUserHome != null )
108+ {
109+ _dockerEnvironment .put ("HOME" , _alternateUserHome );
110+ }
111+
112+ _ctx .getDockerVolumes ().forEach (v -> writer .println ("\t -v '" + v + "':'" + v + "' \\ " ));
113+ if (inputFiles != null )
114+ {
115+ inspectInputFiles (inputFiles ).forEach (v -> writer .println ("\t -v '" + v + "':'" + v + "' \\ " ));
116+ }
117+
56118 if (_tmpDir != null )
57119 {
58- writer .println ("\t -v \" " + _tmpDir .getPath () + ":/tmp\" \\ " );
120+ // NOTE: getDockerVolumes() should be refactored to remove the -v and this logic should be updated accordingly:
121+ if (_ctx .getDockerVolumes ().stream ().noneMatch (_tmpDir .getPath ()::startsWith ))
122+ {
123+ writer .println ("\t -v '" + _tmpDir .getPath () + "':/tmp \\ " );
124+ }
125+ else
126+ {
127+ _ctx .getLogger ().debug ("tmpDir already present in docker volumes, omitting" );
128+ }
129+
130+ addToDockerEnvironment ("TMPDIR" , _tmpDir .getPath ());
131+ }
132+
133+ if (_entryPoint != null )
134+ {
135+ writer .println ("\t --entrypoint \" " + _entryPoint + "\" \\ " );
59136 }
60- writer .println ("\t --entrypoint /bin/bash \\ " );
61- writer .println ("\t -w /work \\ " );
137+
138+ writer .println ("\t -w " + workDir .getPath () + " \\ " );
139+ addToDockerEnvironment ("WORK_DIR" , workDir .getPath ());
140+
62141 Integer maxRam = SequencePipelineService .get ().getMaxRam ();
63142 if (maxRam != null )
64143 {
65144 writer .println ("\t -e SEQUENCEANALYSIS_MAX_RAM=" + maxRam + " \\ " );
66145 writer .println ("\t --memory='" + maxRam + "g' \\ " );
67146 }
147+
148+ for (String key : _dockerEnvironment .keySet ())
149+ {
150+ writer .println ("\t -e " + key + "='" + _dockerEnvironment .get (key ) + "' \\ " );
151+ }
68152 writer .println ("\t " + _containerName + " \\ " );
69- writer .println ("\t /work/ " + dockerBashScript .getName ());
70- writer .println ("EXIT_CODE =$?" );
71- writer .println ("echo 'Docker run exit code: '$EXIT_CODE " );
72- writer .println ("exit $EXIT_CODE " );
153+ writer .println ("\t " + dockerBashScript .getPath ());
154+ writer .println ("DOCKER_EXIT_CODE =$?" );
155+ writer .println ("echo 'Docker run exit code: '$DOCKER_EXIT_CODE " );
156+ writer .println ("exit $DOCKER_EXIT_CODE " );
73157
74158 dockerWriter .println ("#!/bin/bash" );
75159 dockerWriter .println ("set -x" );
76160 dockerWriter .println (StringUtils .join (containerArgs , " " ));
77- dockerWriter .println ("EXIT_CODE =$?" );
78- dockerWriter .println ("echo 'Exit code: '$? " );
79- dockerWriter .println ("exit $EXIT_CODE " );
161+ dockerWriter .println ("BASH_EXIT_CODE =$?" );
162+ dockerWriter .println ("echo 'Bash exit code: '$BASH_EXIT_CODE " );
163+ dockerWriter .println ("exit $BASH_EXIT_CODE " );
80164 }
81165 catch (IOException e )
82166 {
83167 throw new PipelineJobException (e );
84168 }
85169
170+ localBashScript .setExecutable (true );
171+ dockerBashScript .setExecutable (true );
86172 execute (Arrays .asList ("/bin/bash" , localBashScript .getPath ()));
87173 }
88174
89- public File ensureLocalCopy ( File input , File workingDirectory , PipelineOutputTracker output ) throws PipelineJobException
175+ public void addToDockerEnvironment ( String key , String value )
90176 {
91- try
177+ _dockerEnvironment .put (key , value );
178+ }
179+
180+ private Collection <File > inspectInputFiles (Collection <File > inputFiles )
181+ {
182+ Set <File > toAdd = inputFiles .stream ().map (f -> f .isDirectory () ? f : f .getParentFile ()).filter (x -> _ctx .getDockerVolumes ().stream ().noneMatch (x .getPath ()::startsWith )).collect (Collectors .toSet ());
183+ if (!toAdd .isEmpty ())
92184 {
93- if (workingDirectory .equals (input .getParentFile ()))
94- {
95- return input ;
96- }
185+ Set <File > paths = new HashSet <>();
186+ toAdd .forEach (x -> {
187+ _ctx .getLogger ().debug ("Adding volume for path: " + x .getPath ());
97188
98- File local = new File (workingDirectory , input .getName ());
99- if (!local .exists ())
100- {
101- getLogger ().debug ("Copying file locally: " + input .getPath ());
102- FileUtils .copyFile (input , local );
103- }
189+ File converted = SequencePipelineService .get ().inferDockerVolume (x );
190+ if (!x .equals (converted ))
191+ {
192+ _ctx .getLogger ().debug ("added as: " + converted .getPath ());
193+ }
104194
105- output .addIntermediateFile (local );
195+ if (_ctx .getDockerVolumes ().stream ().noneMatch (converted .getPath ()::startsWith ))
196+ {
197+ paths .add (converted );
198+ }
199+ });
106200
107- return local ;
108- }
109- catch (IOException e )
110- {
111- throw new PipelineJobException (e );
201+ return paths ;
112202 }
203+
204+ return Collections .emptySet ();
113205 }
114206}
0 commit comments