Skip to content

Commit c847a3a

Browse files
committed
Container.groovy
* stopAndRemoveContainer() now fills container instead of stopping DirectorySyncer.groovy * Fixed bug that made getDuplicateContainer() return broken containers * Added createSyncVolumeToVolume() and syncBetweenVolumesAndUsers() DockerClientDS.groovy * Added getOrCreateVolume()
1 parent 66a46c3 commit c847a3a

File tree

7 files changed

+158
-31
lines changed

7 files changed

+158
-31
lines changed

.idea/jarRepositories.xml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pom.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<spock-core.version>2.3-groovy-${groovy.major.version}</spock-core.version>
2424
<jiraShortcuts.version>2.0.3-SNAPSHOT-groovy-3.0</jiraShortcuts.version>
2525
<bitbucketinstancemanager.version>0.0.3-SNAPSHOT-groovy-3.0</bitbucketinstancemanager.version>
26-
<jirainstancemanager.version>2.1.8</jirainstancemanager.version>
26+
<jirainstancemanager.version>2.1.9</jirainstancemanager.version>
2727
</properties>
2828

2929

@@ -111,7 +111,7 @@
111111
</dependency>
112112

113113

114-
<dependency>
114+
<!--dependency>
115115
<groupId>org.junit.jupiter</groupId>
116116
<artifactId>junit-jupiter-api</artifactId>
117117
<version>5.10.2</version>
@@ -122,14 +122,15 @@
122122
<artifactId>junit-jupiter-engine</artifactId>
123123
<version>5.10.2</version>
124124
<scope>test</scope>
125-
</dependency>
125+
</dependency-->
126126

127127

128128

129129

130130
</dependencies>
131131

132132
<repositories>
133+
133134
<repository>
134135
<id>eficode-github-jiraManagerRest</id>
135136
<url>https://github.com/eficode/JiraInstanceManagerRest/raw/packages/repository/</url>

src/main/groovy/com/eficode/devstack/container/Container.groovy

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,7 @@ trait Container {
476476

477477

478478
if (self.isRunning()) {
479-
dockerClient.stop(self.containerId, timeoutS)
480-
if (self.isRunning()) {
481-
dockerClient.kill(self.containerId)
482-
}
479+
dockerClient.kill(self.containerId)
483480
}
484481

485482
dockerClient.rm(self.containerId)

src/main/groovy/com/eficode/devstack/deployment/impl/JsmDevDeployment.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class JsmDevDeployment implements Deployment {
9696
boolean setupDeployment() {
9797

9898

99-
srcSyncer = DirectorySyncer.createSyncToVolume(srcCodePaths, srcCodeVolume.name, "-avh --chown=2001:2001")
99+
srcSyncer = DirectorySyncer.createSyncToVolume(srcCodePaths, srcCodeVolume.name, "SrcSyncer", "-avh --chown=2001:2001")
100100
allureContainer.created ?: allureContainer.createContainer()
101101
allureContainer.startContainer()
102102

src/main/groovy/com/eficode/devstack/util/DirectorySyncer.groovy

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.eficode.devstack.util
33
import com.eficode.devstack.container.Container
44
import com.fasterxml.jackson.databind.ObjectMapper
55
import de.gesellix.docker.client.EngineResponseContent
6+
import de.gesellix.docker.client.network.ManageNetworkClient
67
import de.gesellix.docker.remote.api.ContainerSummary
78
import de.gesellix.docker.remote.api.Mount
89
import de.gesellix.docker.remote.api.MountPoint
@@ -33,13 +34,14 @@ class DirectorySyncer implements Container {
3334
*/
3435
DirectorySyncer (DockerClientDS dockerClient, ContainerSummary summary) {
3536

36-
DirectorySyncer syncer = new DirectorySyncer(dockerClient.host, dockerClient.certPath)
37-
syncer.containerName = summary.names.first().replaceFirst("/", "")
37+
this.dockerClient = dockerClient
38+
this.networkClient = dockerClient.getManageNetwork() as ManageNetworkClient
39+
this.containerName = summary.names.first().replaceFirst("/", "")
3840

3941

4042
}
4143

42-
static String getSyncScript(String rsyncOptions = "-avh") {
44+
static String getSyncScript(String rsyncOptions = "-avh", String rsyncSrc = " /mnt/src/", String rsyncDest = " /mnt/dest/") {
4345

4446
return """
4547
@@ -57,7 +59,7 @@ class DirectorySyncer implements Container {
5759
5860
function execute() {
5961
eval "\$@"
60-
rsync $rsyncOptions /mnt/src/*/ /mnt/dest/
62+
rsync $rsyncOptions $rsyncSrc $rsyncDest
6163
}
6264
6365
execute""
@@ -105,7 +107,8 @@ class DirectorySyncer implements Container {
105107
*/
106108
DirectorySyncer getDuplicateContainer() {
107109

108-
Map filterMap = [name: ["DirectorySyncer.*"], "volume": this.preparedMounts.collect { it.target }]
110+
111+
Map filterMap = [name: [this.containerName + ".*"], "volume": this.preparedMounts.collect { it.target }]
109112
String filterString = new ObjectMapper().writeValueAsString(filterMap)
110113
ArrayList<ContainerSummary> looselyMatchingContainers = dockerClient.ps(true, null, false, filterString).content
111114
ArrayList<ContainerSummary> matchingContainers = []
@@ -175,25 +178,15 @@ class DirectorySyncer implements Container {
175178
* @param dockerCertPath Docker certs to use
176179
* @return
177180
*/
178-
static DirectorySyncer createSyncToVolume(ArrayList<String> hostAbsSourcePaths, String destVolumeName, String rsyncOptions = "-avh", String containerName = "",String dockerHost = "", String dockerCertPath = "") {
181+
static DirectorySyncer createSyncToVolume(ArrayList<String> hostAbsSourcePaths, String destVolumeName, String containerName, String rsyncOptions = "-avh", String dockerHost = "", String dockerCertPath = "") {
179182

180183
DirectorySyncer container = new DirectorySyncer(dockerHost, dockerCertPath)
181184
Logger log = container.log
182185

183-
container.containerName = container.getAvailableContainerName()
184-
container.prepareCustomEnvVar(["syncScript=${getSyncScript(rsyncOptions)}"])
185-
186-
Volume volume = container.dockerClient.getVolumesWithName(destVolumeName).find { true }
186+
container.containerName = containerName ?: container.getAvailableContainerName()
187+
container.prepareCustomEnvVar(["syncScript=${getSyncScript(rsyncOptions, "/mnt/src/*/")}"])
187188

188-
if (volume) {
189-
log.debug("\tFound existing volume:" + volume.name)
190-
} else {
191-
log.debug("\tCreating new volume $destVolumeName")
192-
EngineResponseContent<Volume> volumeResponse = container.dockerClient.createVolume(destVolumeName)
193-
volume = volumeResponse?.content
194-
assert volume: "Error creating volume $destVolumeName, " + volumeResponse?.getStatus()?.text
195-
log.debug("\t\tCreated volume:" + volume.name)
196-
}
189+
Volume volume = container.dockerClient.getOrCreateVolume(destVolumeName)
197190

198191
container.prepareVolumeMount(volume.name, "/mnt/dest/", false)
199192

@@ -224,5 +217,62 @@ class DirectorySyncer implements Container {
224217
return container
225218
}
226219

220+
/**
221+
* Creates a DirectorySyncer intended to sync files between two volumes and replacing the owner of the synced files so that
222+
* the destination container user has access to.
223+
* @param srcVolumeName The volume to sync from (the root of this will be synced)
224+
* @param destVolumeName The destination volume where files should be synced to, and where the owner will be changed
225+
* @param destUser The destination user and group that the file owner will be changed to, ex: 1001:1001
226+
* @return
227+
*/
228+
static DirectorySyncer syncBetweenVolumesAndUsers(String srcVolumeName, String destVolumeName, String destUser) {
229+
230+
DirectorySyncer syncer = createSyncVolumeToVolume(srcVolumeName, destVolumeName, "-avhog --chown $destUser")
231+
232+
return syncer
233+
}
234+
235+
236+
/**
237+
* Creates a DirectorySyncer which synces files between the roots of two docker volumes
238+
* @param srcVolumeName The source volume to sync from
239+
* @param destVolumeName The destination volume to sync to
240+
* @param rsyncOptions Options to pass to rsync, default: -avh
241+
* @param containerName Name of the sync container
242+
* @param dockerHost
243+
* @param dockerCertPath
244+
* @return
245+
*/
246+
static DirectorySyncer createSyncVolumeToVolume(String srcVolumeName, String destVolumeName, String rsyncOptions = "-avh", String containerName = "",String dockerHost = "", String dockerCertPath = "") {
247+
248+
DirectorySyncer container = new DirectorySyncer(dockerHost, dockerCertPath)
249+
Logger log = container.log
250+
251+
container.containerName = containerName ?: container.getAvailableContainerName()
252+
container.prepareCustomEnvVar(["syncScript=${getSyncScript(rsyncOptions)}"])
253+
254+
Volume destVolume = container.dockerClient.getOrCreateVolume(destVolumeName)
255+
Volume srcVolume = container.dockerClient.getOrCreateVolume(srcVolumeName)
256+
257+
container.prepareVolumeMount(srcVolume.name, "/mnt/src/", false)
258+
container.prepareVolumeMount(destVolume.name, "/mnt/dest/", false)
259+
260+
261+
DirectorySyncer duplicate = container.getDuplicateContainer()
262+
if (duplicate) {
263+
log.info("\tFound an existing DirectorySyncer with same mount points:" + duplicate.shortId)
264+
if (!duplicate.running) {
265+
log.debug("\t" * 2 + "Duplicate is not running, starting it")
266+
duplicate.startContainer()
267+
}
268+
log.info("\t" * 2 + "Returning duplicate instead of creating a new one")
269+
return duplicate
270+
}
271+
272+
container.createContainer(["/bin/sh", "-c", "echo \"\$syncScript\" > /syncScript.sh && /bin/sh syncScript.sh"], [])
273+
container.startContainer()
274+
275+
return container
276+
}
227277

228278
}

src/main/groovy/com/eficode/devstack/util/DockerClientDS.groovy

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ class DockerClientDS extends DockerClientImpl {
7676
}
7777

7878

79+
Volume getOrCreateVolume(String volumeName) {
80+
Volume volume = getVolumesWithName(volumeName).find { true }
81+
82+
if (volume) {
83+
log.debug("\tFound existing volume:" + volume.name)
84+
} else {
85+
log.debug("\tCreating new volume $volumeName")
86+
EngineResponseContent<Volume> volumeResponse = createVolume(volumeName)
87+
volume = volumeResponse?.content
88+
assert volume: "Error creating volume $volumeName, " + volumeResponse?.getStatus()?.text
89+
log.debug("\t\tCreated destination volume:" + volume.name)
90+
}
91+
92+
return volume
93+
}
94+
7995
ArrayList<Volume> getVolumesWithName(String name) {
8096
EngineResponseContent<VolumeListResponse> response = volumes("{\"name\":[\"$name\"]}")
8197

src/test/groovy/com/eficode/devstack/container/impl/DirectorySyncerTest.groovy

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package com.eficode.devstack.container.impl
22

33
import com.eficode.devstack.DevStackSpec
44
import com.eficode.devstack.util.DirectorySyncer
5+
import com.eficode.devstack.util.ImageSummaryDS
56
import de.gesellix.docker.remote.api.ContainerInspectResponse
67
import de.gesellix.docker.remote.api.MountPoint
8+
import de.gesellix.docker.remote.api.Volume
79
import org.slf4j.LoggerFactory
810

911

@@ -42,13 +44,13 @@ class DirectorySyncerTest extends DevStackSpec {
4244
!volumeExists(uniqueVolumeName) ?: dockerClient.rmVolume(uniqueVolumeName)
4345
log.debug("\tWill use sync to Docker volume:" + uniqueVolumeName)
4446

45-
DirectorySyncer firstSyncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "-avh --delete", dockerRemoteHost, dockerCertPath )
47+
DirectorySyncer firstSyncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "DirSyncer", "-avh --delete", dockerRemoteHost, dockerCertPath )
4648
log.info("\tCreated first sync container: ${firstSyncer.containerName} (${firstSyncer.shortId})")
4749
Integer containersAfterFirst = firstSyncer.dockerClient.ps(true).content.size()
4850
log.info("\t\tDocker engine now has a total of ${containersAfterFirst} contianers")
4951

5052
when: "Creating second sync container"
51-
DirectorySyncer secondSyncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "-avh --delete", dockerRemoteHost, dockerCertPath )
53+
DirectorySyncer secondSyncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "DirSyncer","-avh --delete", dockerRemoteHost, dockerCertPath )
5254
log.info("\tCreated second sync container: ${secondSyncer.containerName} (${secondSyncer.shortId})")
5355

5456
then: "They should have the same ID"
@@ -60,7 +62,7 @@ class DirectorySyncerTest extends DevStackSpec {
6062
when: "Stopping the sync container, and creating another duplicate"
6163
firstSyncer.stopContainer()
6264
assert !firstSyncer.running
63-
secondSyncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "-avh --delete", dockerRemoteHost, dockerCertPath )
65+
secondSyncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "DirSyncer","-avh --delete", dockerRemoteHost, dockerCertPath )
6466

6567
then:"The duplicate should have been automatically started"
6668
secondSyncer.running
@@ -74,6 +76,62 @@ class DirectorySyncerTest extends DevStackSpec {
7476

7577

7678

79+
}
80+
81+
82+
def "Test create createSyncVolumeToVolume"() {
83+
84+
setup:
85+
log.info("Testing createSyncVolumeToVolume")
86+
Volume srcVolume = dockerClient.getOrCreateVolume("srcVolume" + System.nanoTime().toString().takeRight(3))
87+
Volume destVolume = dockerClient.getOrCreateVolume("destVolume" + System.nanoTime().toString().takeRight(3))
88+
log.info("\tWill use src volume:" + srcVolume)
89+
log.info("\tWill use dest volume:" + destVolume)
90+
91+
92+
when: "Creating two containers with two different default users"
93+
UbuntuContainer srcContainer = new UbuntuContainer()
94+
srcContainer.containerName = "SrcContainer"
95+
srcContainer.prepareVolumeMount(srcVolume.name, "/mnt/volume", false)
96+
srcContainer.user = "1001:1001"
97+
srcContainer.createSleepyContainer()
98+
srcContainer.startContainer()
99+
srcContainer.runBashCommandInContainer(ImageSummaryDS.getReplaceUserScriptBody("ubuntu", "1000", "ubuntu", "1000", "ubuntusrc", "1001", "ubuntusrc", "1001"),10, "root" )
100+
srcContainer.runBashCommandInContainer("chown 1001:1001 -R /mnt/volume", 5, "root")
101+
102+
103+
UbuntuContainer destContainer = new UbuntuContainer()
104+
destContainer.containerName = "DestContainer"
105+
destContainer.prepareVolumeMount(destVolume.name, "/mnt/volume", false)
106+
destContainer.user = "1002:1002"
107+
destContainer.createSleepyContainer()
108+
destContainer.startContainer()
109+
destContainer.runBashCommandInContainer(ImageSummaryDS.getReplaceUserScriptBody("ubuntu", "1000", "ubuntu", "1000", "ubuntudest", "1002", "ubuntudest", "1002"),10, "root" )
110+
destContainer.runBashCommandInContainer("chown 1002:1002 -R /mnt/volume", 5, "root")
111+
112+
then: "When checking user id, they should be different"
113+
srcContainer.runBashCommandInContainer("id -u").contains("1001")
114+
destContainer.runBashCommandInContainer("id -u").contains("1002")
115+
116+
117+
when: "Creating the syncer"
118+
119+
DirectorySyncer syncer = DirectorySyncer.syncBetweenVolumesAndUsers(srcVolume.name, destVolume.name, "1002:1002")
120+
srcContainer.runBashCommandInContainer("touch /mnt/volume/createdInSource")
121+
sleep(1000)
122+
then:
123+
destContainer.runBashCommandInContainer("ls -l /mnt/volume/createdInSource && echo Status: \$?").contains("Status: 0")
124+
destContainer.runBashCommandInContainer("echo edited >> /mnt/volume/createdInSource && echo Status: \$?").contains("Status: 0")
125+
destContainer.runBashCommandInContainer("rm /mnt/volume/createdInSource && echo Status: \$?").contains("Status: 0")
126+
127+
128+
cleanup:
129+
srcContainer.stopAndRemoveContainer()
130+
destContainer.stopAndRemoveContainer()
131+
syncer.stopAndRemoveContainer()
132+
dockerClient.manageVolume.rmVolume(srcVolume.name)
133+
dockerClient.manageVolume.rmVolume(destVolume.name)
134+
77135
}
78136

79137
def "Test createSyncToVolume"() {
@@ -93,7 +151,7 @@ class DirectorySyncerTest extends DevStackSpec {
93151
when: "When creating syncer"
94152

95153
assert !volumeExists(uniqueVolumeName): "Destination volume already exists"
96-
DirectorySyncer syncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "-avh --delete", dockerRemoteHost, dockerCertPath )
154+
DirectorySyncer syncer = DirectorySyncer.createSyncToVolume([srcDir1.canonicalPath, srcDir2.canonicalPath], uniqueVolumeName, "DirSyncer", "-avh --delete", dockerRemoteHost, dockerCertPath )
97155
log.info("\tCreated sync container: ${syncer.containerName} (${syncer.shortId})")
98156
ContainerInspectResponse containerInspect = syncer.inspectContainer()
99157

0 commit comments

Comments
 (0)