1
1
package com.eficode.devstack.util
2
2
3
3
import com.eficode.devstack.container.Container
4
+ import com.fasterxml.jackson.databind.ObjectMapper
4
5
import de.gesellix.docker.client.EngineResponseContent
5
6
import de.gesellix.docker.remote.api.ContainerSummary
7
+ import de.gesellix.docker.remote.api.Mount
8
+ import de.gesellix.docker.remote.api.MountPoint
6
9
import de.gesellix.docker.remote.api.Volume
7
10
import org.slf4j.Logger
8
11
@@ -23,6 +26,19 @@ class DirectorySyncer implements Container {
23
26
}
24
27
}
25
28
29
+ /**
30
+ * Create a DirectorySyncer based on existing container
31
+ * @param dockerClient
32
+ * @param summary
33
+ */
34
+ DirectorySyncer (DockerClientDS dockerClient, ContainerSummary summary) {
35
+
36
+ DirectorySyncer syncer = new DirectorySyncer (dockerClient. host, dockerClient. certPath)
37
+ syncer. containerName = summary. names. first(). replaceFirst(" /" , " " )
38
+
39
+
40
+ }
41
+
26
42
static String getSyncScript (String rsyncOptions = " -avh" ) {
27
43
28
44
return """
@@ -80,10 +96,56 @@ class DirectorySyncer implements Container {
80
96
81
97
}
82
98
99
+
100
+
101
+ /**
102
+ * Checks if there already exists a DirectorySyncer container with the same mount points,
103
+ * if found will return that container
104
+ * @return
105
+ */
106
+ DirectorySyncer getDuplicateContainer () {
107
+
108
+ Map filterMap = [name : [" DirectorySyncer.*" ], " volume" : this . preparedMounts. collect { it. target }]
109
+ String filterString = new ObjectMapper (). writeValueAsString(filterMap)
110
+ ArrayList<ContainerSummary > looselyMatchingContainers = dockerClient. ps(true , null , false , filterString). content
111
+ ArrayList<ContainerSummary > matchingContainers = []
112
+ ArrayList<String > myMounts = this . preparedMounts. target
113
+ myMounts + = this . preparedMounts. findAll {it. type == Mount.Type.Volume }. source
114
+ if (looselyMatchingContainers) {
115
+ matchingContainers = looselyMatchingContainers. findAll { matchingContainer ->
116
+
117
+ ArrayList<String > matchingMounts = matchingContainer. mounts. destination
118
+ matchingMounts + = matchingContainer. mounts. findAll {it. type == MountPoint.Type.Volume }. name
119
+ // Handles the fact the mount points arent always given with a trailing /
120
+ Boolean mountsMatch = myMounts. every { myMount ->
121
+ matchingMounts. any { it. equalsIgnoreCase(myMount) } ||
122
+ matchingMounts. collect { it + " /" }. any { it. equalsIgnoreCase(myMount) }
123
+ }
124
+
125
+ return mountsMatch
126
+
127
+ }
128
+ }
129
+
130
+ if (matchingContainers. size() > 1 ) {
131
+ throw new InputMismatchException (" Found multiple potential duplicate DirectorySyncer´s: " + matchingContainers. collect { it. id }. join(" ," ))
132
+ } else if (matchingContainers. size() == 1 ) {
133
+ return new DirectorySyncer (dockerClient, matchingContainers. first())
134
+ } else {
135
+ return null
136
+ }
137
+
138
+ }
139
+
83
140
/**
84
141
* <pre >
85
- * Creates a Util container:
86
- * 1. Listens for file changes in one or more docker engine src paths (hostAbsSourcePaths)
142
+ * This UtilContainer is intended to be used to sync one or several docker engine local dirs
143
+ * to a Docker volume continuously
144
+ *
145
+ * If a DirectorySyncer with the same mount points exists, it will be started and returned instead
146
+ *
147
+ * The container will :
148
+ * 1. Listen for file changes in one or more docker engine src paths recursively (hostAbsSourcePaths)
87
149
* 2. If changes are detected rsync is triggered
88
150
* 3. Rsync detects changes and sync them to destVolumeName
89
151
*
@@ -134,9 +196,25 @@ class DirectorySyncer implements Container {
134
196
135
197
container. prepareVolumeMount(volume. name, " /mnt/dest/" , false )
136
198
137
- hostAbsSourcePaths. each { srcPath ->
199
+ hostAbsSourcePaths. eachWithIndex { srcPath , index ->
200
+
138
201
String srcDirName = srcPath. substring(srcPath. lastIndexOf(" /" ) + 1 )
139
- container. prepareBindMount(srcPath, " /mnt/src/$srcDirName " , true )
202
+ String targetPath = " /mnt/src/$srcDirName "
203
+ if (container. preparedMounts. any { it. target == targetPath }) {
204
+ targetPath = targetPath + index // Make sure target path is unique
205
+ }
206
+ container. prepareBindMount(srcPath, targetPath, true )
207
+ }
208
+
209
+ DirectorySyncer duplicate = container. getDuplicateContainer()
210
+ if (duplicate) {
211
+ log. info(" \t Found an existing DirectorySyncer with same mount points:" + duplicate. shortId)
212
+ if (! duplicate. running) {
213
+ log. debug(" \t " * 2 + " Duplicate is not running, starting it" )
214
+ duplicate. startContainer()
215
+ }
216
+ log. info(" \t " * 2 + " Returning duplicate instead of creating a new one" )
217
+ return duplicate
140
218
}
141
219
142
220
container. createContainer([" /bin/sh" , " -c" , " echo \"\$ syncScript\" > /syncScript.sh && /bin/sh syncScript.sh" ], [])
0 commit comments