1+ package com.eficode.devstack.container
2+
3+ import de.gesellix.docker.client.DockerClientImpl
4+ import de.gesellix.docker.engine.DockerClientConfig
5+ import de.gesellix.docker.engine.DockerEnv
6+ import de.gesellix.docker.engine.EngineResponse
7+ import de.gesellix.docker.remote.api.IdResponse
8+ import de.gesellix.docker.remote.api.core.ClientException
9+ import de.gesellix.docker.remote.api.core.Frame
10+ import de.gesellix.docker.remote.api.core.StreamCallback
11+ import org.apache.commons.compress.archivers.ArchiveEntry
12+ import org.apache.commons.compress.archivers.tar.TarArchiveEntry
13+ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
14+ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
15+ import org.apache.commons.compress.utils.IOUtils
16+ import org.apache.commons.io.FileUtils
17+ import org.slf4j.Logger
18+ import org.slf4j.LoggerFactory
19+
20+ import java.nio.file.Files
21+ import java.nio.file.Path
22+ import java.time.Duration
23+
24+ trait ContainerManager {
25+
26+ static Logger log = LoggerFactory . getLogger(ContainerManager . class)
27+ static DockerClientImpl dockerClient = new DockerClientImpl ()
28+ abstract String containerName
29+ abstract String containerMainPort
30+ String containerId
31+
32+
33+ abstract String createContainer()
34+
35+
36+
37+
38+
39+
40+ /**
41+ * Replaced the default docker connection (local) with a remote, secure one
42+ * @param host ex: "https://docker.domain.se:2376"
43+ * @param certPath folder containing ca.pem, cert.pem, key.pem
44+ */
45+ static DockerClientImpl setupSecureRemoteConnection(String host, String certPath) {
46+
47+ DockerClientConfig dockerConfig = new DockerClientConfig (host)
48+ DockerEnv dockerEnv = new DockerEnv (host)
49+ dockerEnv. setCertPath(certPath)
50+ dockerEnv. setTlsVerify(" 1" )
51+ dockerConfig. apply(dockerEnv)
52+
53+ return new DockerClientImpl (dockerConfig)
54+
55+ }
56+
57+
58+ boolean ping() {
59+ return dockerClient. ping(). content as String == " OK"
60+ }
61+ String getContainerId() {
62+
63+ if (containerId) {
64+ return containerId
65+ }
66+ log. info(" \t Resolving container ID for: $containerName " )
67+
68+ ArrayList<Map > content = dockerClient. ps(). content
69+
70+ Map container = content. find { it.Names . first() == " /" + containerName }
71+ this . containerId = container?. Id
72+ log. info(" \t Got:" + this . containerId)
73+
74+ return containerId
75+ }
76+
77+ boolean startContainer() {
78+
79+ dockerClient. startContainer(containerId)
80+
81+ return dockerClient. inspectContainer(containerId). content. state. running
82+ }
83+
84+ boolean stopAndRemoveContainer() {
85+
86+ dockerClient. stop(containerId, 240000 )
87+ dockerClient. wait(containerId)
88+ dockerClient. rm(containerId)
89+
90+
91+ try {
92+ dockerClient. inspectContainer(containerId)
93+ } catch (ClientException ex) {
94+
95+ if (ex. response. message == " Not Found" ) {
96+ return true
97+ }
98+ }
99+ return false
100+
101+
102+ }
103+
104+
105+
106+
107+ static File createTar(ArrayList<String > filePaths, String outputPath) {
108+
109+
110+ File outputFile = new File (outputPath)
111+ TarArchiveOutputStream out = new TarArchiveOutputStream (Files . newOutputStream(outputFile. toPath()))
112+
113+ filePaths. each { filePath ->
114+ File newEntryFile = new File (filePath)
115+ assert newEntryFile. isFile() && newEntryFile. canRead(), " Error creating TAR cant read file:" + filePath
116+
117+
118+ TarArchiveEntry entry = new TarArchiveEntry (newEntryFile, newEntryFile. name)
119+ entry. setSize(newEntryFile. size())
120+ out. putArchiveEntry(entry)
121+ out. write(newEntryFile. bytes)
122+
123+ out. closeArchiveEntry()
124+ }
125+
126+ out. finish()
127+
128+ return outputFile
129+
130+
131+ }
132+
133+ static ArrayList<File > extractTar(File tarFile, String outputPath) {
134+
135+
136+
137+ log. info(" Extracting: " + tarFile. path + " (${ tarFile.size()/1024} kB)" )
138+ log. info(" \t To:" + outputPath)
139+ assert outputPath[-1 ] == " /" , " outputPath must end with /"
140+ ArrayList<File > outFiles = []
141+ TarArchiveInputStream i = new TarArchiveInputStream (tarFile. newInputStream())
142+
143+ ArchiveEntry entry = null
144+
145+ while ((entry = i. getNextEntry()) != null ) {
146+ String outName = outputPath + entry. name
147+ log. debug(" \t Extracting compressed file:" + entry. name + " , to:" + outputPath)
148+
149+ File outFile = new File (outName)
150+
151+ if (entry. isDirectory()) {
152+ assert outFile. mkdirs(), " Error creating directory:" + outFile. path
153+ }else {
154+ outFile. parentFile. mkdirs()
155+ OutputStream o = Files . newOutputStream(outFile. toPath())
156+ long output = IOUtils . copy(i,o)
157+ log. trace(" \t\t Extracted ${ (output/1024).round(1)} kB" )
158+ o. close()
159+ }
160+ outFiles. add(outFile)
161+ }
162+
163+ return outFiles
164+ }
165+
166+
167+ /**
168+ * Copy files from a container
169+ * @param containerPath can be a file or a path (ending in /)
170+ * @param destinationPath
171+ * @return
172+ */
173+ ArrayList<File > copyFilesFromContainer(String containerPath, String destinationPath) {
174+
175+ // containerPath can be both a directory or a file
176+ EngineResponse<InputStream > response = dockerClient. getArchive(containerId, containerPath)
177+
178+
179+ Path tempFile = Files . createTempFile(" docker_download" , " .tar" )
180+ FileUtils . copyInputStreamToFile(response. content, tempFile. toFile())
181+
182+ ArrayList<File > outFiles = extractTar(tempFile. toFile(), destinationPath)
183+
184+ Files . delete(tempFile)
185+
186+ return outFiles
187+
188+ }
189+
190+ boolean copyFileToContainer(String srcFilePath, String destinationDirectory) {
191+
192+
193+ File tarFile = createTar([srcFilePath], Files . createTempFile(" docker_upload" , " .tar" ). toString())
194+ dockerClient. putArchive(containerId, destinationDirectory, tarFile. newDataInputStream())
195+
196+ return tarFile. delete()
197+ }
198+
199+
200+
201+
202+
203+ static class ContainerCallback<T> implements StreamCallback<T> {
204+
205+ ArrayList<String > output = []
206+
207+ @Override
208+ void onNext(Object o) {
209+ if (o instanceof Frame ) {
210+ output. add(o. payloadAsString)
211+ }else {
212+ output. add(o. toString())
213+ }
214+
215+ }
216+ }
217+
218+
219+ ArrayList<String > runBashCommandInContainer(String command, long timeoutS= 10 ) {
220+
221+
222+ ContainerCallback callBack = new ContainerCallback ()
223+ EngineResponse<IdResponse > response = dockerClient. exec(containerId, [" /bin/bash" , " -c" , command],callBack , Duration . ofSeconds(timeoutS))
224+
225+
226+
227+ return callBack. output
228+ }
229+
230+
231+
232+ }
0 commit comments