@@ -13,8 +13,8 @@ import de.gesellix.docker.remote.api.ContainerSummary
13
13
import de.gesellix.docker.remote.api.EndpointSettings
14
14
import de.gesellix.docker.remote.api.ExecConfig
15
15
import de.gesellix.docker.remote.api.HostConfig
16
- import de.gesellix.docker.remote.api.IdResponse
17
16
import de.gesellix.docker.remote.api.Mount
17
+ import de.gesellix.docker.remote.api.MountPoint
18
18
import de.gesellix.docker.remote.api.Network
19
19
import de.gesellix.docker.remote.api.NetworkCreateRequest
20
20
import de.gesellix.docker.remote.api.PortBinding
@@ -52,7 +52,7 @@ trait Container {
52
52
ArrayList<String > customEnvVar = []
53
53
String defaultShell = " /bin/bash"
54
54
String containerId
55
- ArrayList<Mount > mounts = []
55
+ ArrayList<Mount > preparedMounts = [] // Mounts that will be added at creation
56
56
57
57
58
58
/**
@@ -70,11 +70,40 @@ trait Container {
70
70
m. type = Mount.Type.Bind
71
71
}
72
72
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)
75
75
}
76
76
}
77
77
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
+ }
78
107
79
108
ContainerCreateRequest setupContainerCreateRequest() {
80
109
@@ -91,7 +120,7 @@ trait Container {
91
120
if (self. containerMainPort) {
92
121
h. portBindings = [(self. containerMainPort + " /tcp" ): [new PortBinding (" 0.0.0.0" , (self. containerMainPort))]]
93
122
}
94
- h. mounts = self. mounts
123
+ h. mounts = self. preparedMounts
95
124
}
96
125
c. hostname = self. containerName
97
126
c. env = self. customEnvVar
@@ -124,7 +153,7 @@ trait Container {
124
153
ContainerCreateRequest containerCreateRequest = setupContainerCreateRequest()
125
154
126
155
if (cmd. size()) {
127
- containerCreateRequest. cmd = cmd. collect {it. toString()}
156
+ containerCreateRequest. cmd = cmd. collect { it. toString() }
128
157
}
129
158
130
159
if (entrypoint. size()) {
@@ -201,8 +230,16 @@ trait Container {
201
230
202
231
}
203
232
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
+
204
241
String getId() {
205
- return containerId
242
+ return getContainerId()
206
243
}
207
244
208
245
Container getSelf() {
@@ -329,29 +366,33 @@ trait Container {
329
366
330
367
}
331
368
332
- boolean stopContainer() {
369
+ boolean stopContainer(Integer timeoutS = 15 ) {
333
370
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
+
335
374
if (running) {
336
375
log. warn(" \t Failed to stop container" + self. containerId)
376
+ log. warn(" Gave up waiting to shutdown ${ shortId} after ${ System.currentTimeSeconds() - start} seconds" )
337
377
return false
338
378
} else {
339
379
log. info(" \t Container stopped" )
380
+ log. debug(" \t\t Container ${ shortId} stopped after ${ System.currentTimeSeconds() - start} seconds" )
340
381
return true
341
382
}
342
383
}
343
384
344
385
345
- File createTar(ArrayList<String > filePaths, String outputPath) {
386
+ File createTar(ArrayList<String > filePaths, String outputPath, ArrayList< String > ignorePaths = [] ) {
346
387
347
388
348
389
log. info(" Creating tar file:" + outputPath)
349
390
log. debug(" \t Using source paths:" )
350
391
filePaths. each { log. debug(" \t\t $it " ) }
351
392
352
-
353
393
File outputFile = new File (outputPath)
354
394
TarArchiveOutputStream tarArchive = new TarArchiveOutputStream (Files . newOutputStream(outputFile. toPath()))
395
+ tarArchive. setLongFileMode(TarArchiveOutputStream . LONGFILE_POSIX )
355
396
356
397
log. info(" \t Processing files" )
357
398
filePaths. each { filePath ->
@@ -368,28 +409,40 @@ trait Container {
368
409
369
410
String path = ResourceGroovyMethods . relativePath(newEntryFile, subFile)
370
411
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
+
377
424
}
378
425
} else {
379
426
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" )
386
427
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
+ }
387
438
}
388
439
389
440
390
441
}
391
442
392
443
tarArchive. finish()
444
+ log. info(" \t Finished creating TAR file:" + outputFile. absolutePath)
445
+ log. debug(" \t\t " + (outputFile. size() / (1024 * 1024 )). round() + " MB" )
393
446
394
447
return outputFile
395
448
@@ -429,6 +482,15 @@ trait Container {
429
482
}
430
483
431
484
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
+
432
494
/**
433
495
* Creates a network of the type bridge, or returns an existing one if one with the same name exists
434
496
* @param networkName name of the network
@@ -637,12 +699,12 @@ trait Container {
637
699
boolean replaceFileInContainer(String content, String filePath, boolean verify = false ) {
638
700
ArrayList<String > out = runBashCommandInContainer(" cat > $filePath <<- 'EOF'\n " + content + " \n EOF" )
639
701
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 " )
641
703
642
704
if (verify) {
643
- ArrayList<String > rawOut = runBashCommandInContainer(" cat " + filePath)
705
+ ArrayList<String > rawOut = runBashCommandInContainer(" cat " + filePath)
644
706
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"
646
708
return true
647
709
}
648
710
@@ -672,10 +734,18 @@ trait Container {
672
734
673
735
}
674
736
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 = []) {
676
746
677
747
678
- File tarFile = createTar([srcFilePath], Files . createTempFile(" docker_upload" , " .tar" ). toString())
748
+ File tarFile = createTar([srcFilePath], Files . createTempFile(" docker_upload" , " .tar" ). toString(), ignorePaths )
679
749
dockerClient. putArchive(self. containerId, destinationDirectory, tarFile. newDataInputStream())
680
750
681
751
return tarFile. delete()
@@ -684,6 +754,8 @@ trait Container {
684
754
685
755
static class ContainerCallback<T> implements StreamCallback<T> {
686
756
757
+
758
+ Logger log = LoggerFactory . getLogger(ContainerCallback . class)
687
759
ArrayList<String > output = []
688
760
689
761
@Override
@@ -693,6 +765,7 @@ trait Container {
693
765
} else {
694
766
output. add(o. toString())
695
767
}
768
+ log. debug(output. last())
696
769
697
770
}
698
771
}
@@ -759,7 +832,6 @@ trait Container {
759
832
}
760
833
761
834
762
-
763
835
/**
764
836
* Creates a temporary container, runs a command, exits and removes container
765
837
* @param container a container object that hasnt yet been created
@@ -774,20 +846,18 @@ trait Container {
774
846
* @param dockerCertPath
775
847
* @return An array of the container logs, or just an array containing container id if timeoutMs == 0
776
848
*/
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 = " " ) {
778
850
779
851
780
852
Container container = this . getConstructor(String , String ). newInstance(dockerHost, dockerCertPath)
781
853
782
854
Logger log = LoggerFactory . getLogger(this . class)
783
855
784
856
785
-
786
857
log. info(" Creating a $container . class . simpleName and running:" )
787
858
log. info(" \t Cmd:" + cmd)
788
859
789
860
790
-
791
861
try {
792
862
793
863
container. containerName = container. containerName + " -cmd-" + System . currentTimeMillis(). toString()[-5 .. -1 ]
@@ -821,15 +891,14 @@ trait Container {
821
891
log. info(" \t Container finisehd or timed out after ${ System.currentTimeMillis() - start} ms" )
822
892
823
893
if (container. running) {
824
- log. info(" \t " * 2 + " Stopping container forcefully." )
894
+ log. info(" \t " * 2 + " Stopping container forcefully." )
825
895
ArrayList<String > containerOut = container. containerLogs
826
896
assert container. stopAndRemoveContainer(1 ): " Error stopping and removing CMD container after it timed out"
827
897
828
898
throw new TimeoutException (" CMD container timed out, was forcefully stopped and removed. Container logs:" + containerOut?. join(" \n " ))
829
899
}
830
900
831
901
832
-
833
902
ArrayList<String > containerOut = container. containerLogs
834
903
835
904
log. info(" \t Returning ${ containerOut.size()} log lines" )
@@ -843,8 +912,8 @@ trait Container {
843
912
844
913
try {
845
914
container. stopAndRemoveContainer(1 )
846
- } catch (ignored){}
847
-
915
+ } catch (ignored) {
916
+ }
848
917
849
918
850
919
throw ex
0 commit comments