@@ -13,8 +13,8 @@ import de.gesellix.docker.remote.api.ContainerSummary
1313import de.gesellix.docker.remote.api.EndpointSettings
1414import de.gesellix.docker.remote.api.ExecConfig
1515import de.gesellix.docker.remote.api.HostConfig
16- import de.gesellix.docker.remote.api.IdResponse
1716import de.gesellix.docker.remote.api.Mount
17+ import de.gesellix.docker.remote.api.MountPoint
1818import de.gesellix.docker.remote.api.Network
1919import de.gesellix.docker.remote.api.NetworkCreateRequest
2020import de.gesellix.docker.remote.api.PortBinding
@@ -52,7 +52,7 @@ trait Container {
5252 ArrayList<String > customEnvVar = []
5353 String defaultShell = " /bin/bash"
5454 String containerId
55- ArrayList<Mount > mounts = []
55+ ArrayList<Mount > preparedMounts = [] // Mounts that will be added at creation
5656
5757
5858 /**
@@ -70,11 +70,40 @@ trait Container {
7070 m. type = Mount.Type.Bind
7171 }
7272
73- if (! self. mounts . find { it. source == sourceAbs && it. target == target }) {
74- self. mounts . add(newMount)
73+ if (! self. preparedMounts . find { it. source == sourceAbs && it. target == target }) {
74+ self. preparedMounts . add(newMount)
7575 }
7676 }
7777
78+ /**
79+ * Prepare mounting of an existing or new volume
80+ * @param volumeName The name of the volume to create, or an existing one to mount
81+ * @param target Where to mount it in the container
82+ * @param readOnly If it should be read only or not
83+ */
84+ void prepareVolumeMount(String volumeName, String target, boolean readOnly = true ) {
85+
86+ Mount newMount = new Mount (). tap { m ->
87+ m. source = volumeName
88+ m. target = target
89+ m. readOnly = readOnly
90+ m. type = Mount.Type.Volume
91+ }
92+
93+ if (! self. preparedMounts. find { it. source == volumeName && it. target == target }) {
94+ self. preparedMounts. add(newMount)
95+ }
96+ }
97+
98+ /**
99+ * Get MountPoints currently attached to container
100+ * @return
101+ */
102+ ArrayList<MountPoint > getMounts() {
103+
104+ ContainerInspectResponse response = inspectContainer()
105+ return response. mounts
106+ }
78107
79108 ContainerCreateRequest setupContainerCreateRequest() {
80109
@@ -91,7 +120,7 @@ trait Container {
91120 if (self. containerMainPort) {
92121 h. portBindings = [(self. containerMainPort + " /tcp" ): [new PortBinding (" 0.0.0.0" , (self. containerMainPort))]]
93122 }
94- h. mounts = self. mounts
123+ h. mounts = self. preparedMounts
95124 }
96125 c. hostname = self. containerName
97126 c. env = self. customEnvVar
@@ -124,7 +153,7 @@ trait Container {
124153 ContainerCreateRequest containerCreateRequest = setupContainerCreateRequest()
125154
126155 if (cmd. size()) {
127- containerCreateRequest. cmd = cmd. collect {it. toString()}
156+ containerCreateRequest. cmd = cmd. collect { it. toString() }
128157 }
129158
130159 if (entrypoint. size()) {
@@ -201,8 +230,16 @@ trait Container {
201230
202231 }
203232
233+ /**
234+ * Returnes the common short form of the container ID
235+ * @return
236+ */
237+ String getShortId() {
238+ return getContainerId(). substring(0 ,12 )
239+ }
240+
204241 String getId() {
205- return containerId
242+ return getContainerId()
206243 }
207244
208245 Container getSelf() {
@@ -329,29 +366,33 @@ trait Container {
329366
330367 }
331368
332- boolean stopContainer() {
369+ boolean stopContainer(Integer timeoutS = 15 ) {
333370 log. info(" Stopping container:" + self. containerId)
334- running ? dockerClient. stop(self. containerId, 15 ) : " "
371+ long start = System . currentTimeSeconds()
372+ running ? dockerClient. stop(self. containerId, timeoutS) : " "
373+
335374 if (running) {
336375 log. warn(" \t Failed to stop container" + self. containerId)
376+ log. warn(" Gave up waiting to shutdown ${ shortId} after ${ System.currentTimeSeconds() - start} seconds" )
337377 return false
338378 } else {
339379 log. info(" \t Container stopped" )
380+ log. debug(" \t\t Container ${ shortId} stopped after ${ System.currentTimeSeconds() - start} seconds" )
340381 return true
341382 }
342383 }
343384
344385
345- File createTar(ArrayList<String > filePaths, String outputPath) {
386+ File createTar(ArrayList<String > filePaths, String outputPath, ArrayList< String > ignorePaths = [] ) {
346387
347388
348389 log. info(" Creating tar file:" + outputPath)
349390 log. debug(" \t Using source paths:" )
350391 filePaths. each { log. debug(" \t\t $it " ) }
351392
352-
353393 File outputFile = new File (outputPath)
354394 TarArchiveOutputStream tarArchive = new TarArchiveOutputStream (Files . newOutputStream(outputFile. toPath()))
395+ tarArchive. setLongFileMode(TarArchiveOutputStream . LONGFILE_POSIX )
355396
356397 log. info(" \t Processing files" )
357398 filePaths. each { filePath ->
@@ -368,28 +409,40 @@ trait Container {
368409
369410 String path = ResourceGroovyMethods . relativePath(newEntryFile, subFile)
370411 log. trace(" \t " * 4 + " Processing sub file:" + path)
371- TarArchiveEntry entry = new TarArchiveEntry (subFile, path)
372- entry. setSize(subFile. size())
373- tarArchive. putArchiveEntry(entry)
374- tarArchive. write(subFile. bytes)
375- tarArchive. closeArchiveEntry()
376- log. trace(" \t " * 5 + " Added to archive" )
412+ if (ignorePaths. any { subFile. absolutePath. matches(it) }) {
413+ log. trace(" \t " * 5 + " File matches a path that is to be ignored, will not process further" )
414+ } else {
415+ TarArchiveEntry entry = new TarArchiveEntry (subFile, path)
416+ entry. setSize(subFile. size())
417+ tarArchive. putArchiveEntry(entry)
418+ tarArchive. write(subFile. bytes)
419+ tarArchive. closeArchiveEntry()
420+ log. trace(" \t " * 5 + " Added to archive" )
421+ }
422+
423+
377424 }
378425 } else {
379426 log. trace(" \t " * 4 + " Processing file:" + newEntryFile. name)
380- TarArchiveEntry entry = new TarArchiveEntry (newEntryFile, newEntryFile. name)
381- entry. setSize(newEntryFile. size())
382- tarArchive. putArchiveEntry(entry)
383- tarArchive. write(newEntryFile. bytes)
384- tarArchive. closeArchiveEntry()
385- log. trace(" \t " * 5 + " Added to archive" )
386427
428+ if (ignorePaths. any { newEntryFile. absolutePath. matches(it) }) {
429+ log. trace(" \t " * 5 + " File matches a path that is to be ignored, will not process further" )
430+ }else {
431+ TarArchiveEntry entry = new TarArchiveEntry (newEntryFile, newEntryFile. name)
432+ entry. setSize(newEntryFile. size())
433+ tarArchive. putArchiveEntry(entry)
434+ tarArchive. write(newEntryFile. bytes)
435+ tarArchive. closeArchiveEntry()
436+ log. trace(" \t " * 5 + " Added to archive" )
437+ }
387438 }
388439
389440
390441 }
391442
392443 tarArchive. finish()
444+ log. info(" \t Finished creating TAR file:" + outputFile. absolutePath)
445+ log. debug(" \t\t " + (outputFile. size() / (1024 * 1024 )). round() + " MB" )
393446
394447 return outputFile
395448
@@ -429,6 +482,15 @@ trait Container {
429482 }
430483
431484
485+ /**
486+ * Gets the home path for the containers default user
487+ * @return ex: /home/user
488+ */
489+ String getHomePath() {
490+ runBashCommandInContainer(" pwd" ). find {true }
491+ }
492+
493+
432494 /**
433495 * Creates a network of the type bridge, or returns an existing one if one with the same name exists
434496 * @param networkName name of the network
@@ -637,12 +699,12 @@ trait Container {
637699 boolean replaceFileInContainer(String content, String filePath, boolean verify = false ) {
638700 ArrayList<String > out = runBashCommandInContainer(" cat > $filePath <<- 'EOF'\n " + content + " \n EOF" )
639701
640- assert out. isEmpty() : " Unexpected output when replacing file $filePath : " + out. join(" \n " )
702+ assert out. isEmpty(): " Unexpected output when replacing file $filePath : " + out. join(" \n " )
641703
642704 if (verify) {
643- ArrayList<String > rawOut = runBashCommandInContainer(" cat " + filePath)
705+ ArrayList<String > rawOut = runBashCommandInContainer(" cat " + filePath)
644706 String readOut = rawOut. join()
645- assert readOut. trim() == content. trim() : " Error when verifying that the file $filePath was replaced"
707+ assert readOut. trim() == content. trim(): " Error when verifying that the file $filePath was replaced"
646708 return true
647709 }
648710
@@ -672,10 +734,18 @@ trait Container {
672734
673735 }
674736
675- boolean copyFileToContainer(String srcFilePath, String destinationDirectory) {
737+ /**
738+ * Creates a temporary tar, copies it to the container and extracts it there
739+ * @param srcFilePath Local path to copy, will find directories/files recursively
740+ * @param destinationDirectory The destination path in the container, must already exist and be absolut
741+ * @param ignorePaths If these regex patterns matches the path/name of a file it wont be copied over.
742+ * ex: [".*\\.git.*"]
743+ * @return true on success
744+ */
745+ boolean copyFileToContainer(String srcFilePath, String destinationDirectory, ArrayList<String > ignorePaths = []) {
676746
677747
678- File tarFile = createTar([srcFilePath], Files . createTempFile(" docker_upload" , " .tar" ). toString())
748+ File tarFile = createTar([srcFilePath], Files . createTempFile(" docker_upload" , " .tar" ). toString(), ignorePaths )
679749 dockerClient. putArchive(self. containerId, destinationDirectory, tarFile. newDataInputStream())
680750
681751 return tarFile. delete()
@@ -684,6 +754,8 @@ trait Container {
684754
685755 static class ContainerCallback<T> implements StreamCallback<T> {
686756
757+
758+ Logger log = LoggerFactory . getLogger(ContainerCallback . class)
687759 ArrayList<String > output = []
688760
689761 @Override
@@ -693,6 +765,7 @@ trait Container {
693765 } else {
694766 output. add(o. toString())
695767 }
768+ log. debug(output. last())
696769
697770 }
698771 }
@@ -759,7 +832,6 @@ trait Container {
759832 }
760833
761834
762-
763835 /**
764836 * Creates a temporary container, runs a command, exits and removes container
765837 * @param container a container object that hasnt yet been created
@@ -774,20 +846,18 @@ trait Container {
774846 * @param dockerCertPath
775847 * @return An array of the container logs, or just an array containing container id if timeoutMs == 0
776848 */
777- static ArrayList<String > runCmdAndRm( ArrayList<String > cmd, long timeoutMs, ArrayList<Map > mounts = [], String dockerHost = " " , String dockerCertPath = " " ) {
849+ static ArrayList<String > runCmdAndRm(ArrayList<String > cmd, long timeoutMs, ArrayList<Map > mounts = [], String dockerHost = " " , String dockerCertPath = " " ) {
778850
779851
780852 Container container = this . getConstructor(String , String ). newInstance(dockerHost, dockerCertPath)
781853
782854 Logger log = LoggerFactory . getLogger(this . class)
783855
784856
785-
786857 log. info(" Creating a $container . class . simpleName and running:" )
787858 log. info(" \t Cmd:" + cmd)
788859
789860
790-
791861 try {
792862
793863 container. containerName = container. containerName + " -cmd-" + System . currentTimeMillis(). toString()[-5 .. -1 ]
@@ -821,15 +891,14 @@ trait Container {
821891 log. info(" \t Container finisehd or timed out after ${ System.currentTimeMillis() - start} ms" )
822892
823893 if (container. running) {
824- log. info(" \t " * 2 + " Stopping container forcefully." )
894+ log. info(" \t " * 2 + " Stopping container forcefully." )
825895 ArrayList<String > containerOut = container. containerLogs
826896 assert container. stopAndRemoveContainer(1 ): " Error stopping and removing CMD container after it timed out"
827897
828898 throw new TimeoutException (" CMD container timed out, was forcefully stopped and removed. Container logs:" + containerOut?. join(" \n " ))
829899 }
830900
831901
832-
833902 ArrayList<String > containerOut = container. containerLogs
834903
835904 log. info(" \t Returning ${ containerOut.size()} log lines" )
@@ -843,8 +912,8 @@ trait Container {
843912
844913 try {
845914 container. stopAndRemoveContainer(1 )
846- } catch (ignored){}
847-
915+ } catch (ignored) {
916+ }
848917
849918
850919 throw ex
0 commit comments