diff --git a/.github/workflows/publish-maven-package.yml b/.github/workflows/publish-maven-package.yml index 8c2e50a..b6eca14 100644 --- a/.github/workflows/publish-maven-package.yml +++ b/.github/workflows/publish-maven-package.yml @@ -50,7 +50,8 @@ jobs: run: | echo Compiling, Packaging and Installing Groovy 2.5 version of library to local m2 directory - mvn install -f pom.xml -DcreateChecksum=true -Dgroovy.major.version=2.5 -Dgroovy.version=2.5.18 -Dspock-core.version=2.2-groovy-2.5 -DjiraShortcuts.version=2.0-SNAPSHOT-groovy-2.5 -Dbitbucketinstancemanager.version=0.0.3-SNAPSHOT-groovy-2.5 + mvn install -f pom.xml -DcreateChecksum=true -Dgroovy.major.version=2.5 -Dgroovy.version=2.5.18 -Dspock-core.version=2.2-groovy-2.5 -DjiraShortcuts.version=2.0.1-SNAPSHOT-groovy-2.5 -Dbitbucketinstancemanager.version=0.0.3-SNAPSHOT-groovy-2.5 + - name: Copying JAR files run: | diff --git a/Environments/Terraform/main.tf b/Environments/Terraform/main.tf index 90fc945..16a3e9a 100644 --- a/Environments/Terraform/main.tf +++ b/Environments/Terraform/main.tf @@ -288,9 +288,9 @@ resource "aws_lb" "load-balancer" { name = "${var.tags.useCase}-${var.tags.owner}-lb" internal = false - load_balancer_type = "network" + load_balancer_type = "application" subnets = [aws_subnet.base-stack-public-subnet.id] - + enable_deletion_protection = false } diff --git a/pom.xml b/pom.xml index 3b27c8a..211a7ed 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.eficode devstack - 2.0.0-SNAPSHOT-groovy-${groovy.major.version} + 2.0.1-SNAPSHOT-groovy-${groovy.major.version} jar DevStack - Parent pom @@ -39,8 +39,8 @@ - com.eficode.atlassian - JiraShortcuts + com.eficode.atlassian.jira + jiraShortcuts ${jiraShortcuts.version} @@ -80,7 +80,7 @@ com.eficode.atlassian jirainstancemanager - 1.1.0-SNAPSHOT + 1.3.0-SNAPSHOT-groovy-${groovy.major.version} @@ -143,7 +143,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.4.0 + 3.4.1 package @@ -157,28 +157,44 @@ standalone + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + - org.codehaus.groovy:groovy + org.codehaus.groovy:* + + com.google.code.gson:gson org.apache.httpcomponents commons-* + com.kohlschutter.junixsocket:junixsocket-core - + true + true @@ -243,7 +259,7 @@ 3.0 3.0.11 2.2-groovy-3.0 - 2.0-SNAPSHOT-groovy-3.0 + 2.0.1-SNAPSHOT-groovy-3.0 0.0.3-SNAPSHOT-groovy-3.0 diff --git a/src/main/groovy/com/eficode/devstack/container/Container.groovy b/src/main/groovy/com/eficode/devstack/container/Container.groovy index b15d25e..878462a 100644 --- a/src/main/groovy/com/eficode/devstack/container/Container.groovy +++ b/src/main/groovy/com/eficode/devstack/container/Container.groovy @@ -1,6 +1,6 @@ package com.eficode.devstack.container -import de.gesellix.docker.client.DockerClientImpl +import com.eficode.devstack.util.DockerClientDS import de.gesellix.docker.client.EngineResponseContent import de.gesellix.docker.client.network.ManageNetworkClient import de.gesellix.docker.engine.DockerClientConfig @@ -11,6 +11,7 @@ import de.gesellix.docker.remote.api.ContainerInspectResponse import de.gesellix.docker.remote.api.ContainerState import de.gesellix.docker.remote.api.ContainerSummary import de.gesellix.docker.remote.api.EndpointSettings +import de.gesellix.docker.remote.api.ExecConfig import de.gesellix.docker.remote.api.HostConfig import de.gesellix.docker.remote.api.IdResponse import de.gesellix.docker.remote.api.Mount @@ -40,21 +41,20 @@ import java.util.regex.Pattern trait Container { Logger log = LoggerFactory.getLogger(self.class) - DockerClientImpl dockerClient = new DockerClientImpl() + DockerClientDS dockerClient = new DockerClientDS() ManageNetworkClient networkClient = dockerClient.getManageNetwork() as ManageNetworkClient abstract String containerName abstract String containerMainPort abstract String containerImage abstract String containerImageTag + ArrayList containerDefaultNetworks = ["bridge"] ArrayList customEnvVar = [] - //String containerNetworkName = "bridge" String defaultShell = "/bin/bash" String containerId ArrayList mounts = [] void prepareBindMount(String sourceAbs, String target, boolean readOnly = true) { - assert !isCreated(): "Bind mounts cant be prepared for already created container" Mount newMount = new Mount().tap { m -> m.source = sourceAbs @@ -89,7 +89,6 @@ trait Container { c.hostname = self.containerName c.env = self.customEnvVar - } return containerCreateRequest @@ -98,11 +97,11 @@ trait Container { /** * Create container and override default docker cmd and entrypoint - * @param cmd: + * @param cmd : * @param entrypoint ex: ["tail", "-f", "/dev/null"] * @return container id */ - String createContainer(ArrayList cmd, ArrayList entrypoint) { + String createContainer(ArrayList cmd = [], ArrayList entrypoint = []) { assert ping(): "Error connecting to docker engine" @@ -119,14 +118,14 @@ trait Container { EngineResponseContent response = dockerClient.createContainer(containerCreateRequest, self.containerName) assert response.content.warnings.isEmpty(): "Error when creating ${self.containerName} container:" + response.content.warnings.join(",") + ArrayList networks = containerDefaultNetworks.collect { createBridgeNetwork(it) } + assert setContainerNetworks(networks): "Error setting container networks to:" + containerDefaultNetworks + containerId = response.content.id return containerId } - String createContainer() { - return createContainer([], []) - } boolean runOnFirstStartup() { return true @@ -145,7 +144,7 @@ trait Container { dockerEnv.setCertPath(certPath) dockerEnv.setTlsVerify("1") dockerConfig.apply(dockerEnv) - dockerClient = new DockerClientImpl(dockerConfig) + dockerClient = new DockerClientDS(dockerConfig) networkClient = dockerClient.getManageNetwork() as ManageNetworkClient @@ -248,10 +247,8 @@ trait Container { if (status == ContainerState.Status.Created) { return true //Created but not started - } else if (status == null) { - return true //Not even created } else { - return false + return status == null } } @@ -270,7 +267,7 @@ trait Container { } ContainerState.Status status() { - return inspectContainer().state.status + return inspectContainer()?.state?.status } boolean stopAndRemoveContainer(Integer timeoutS = 5) { @@ -469,28 +466,39 @@ trait Container { * @param networkNameOrId * @return Network if found, null if not */ - Network getNetwork(String networkNameOrId) { + Network getDockerNetwork(String networkNameOrId) { Network network = networkClient.networks().content.find { it.name == networkNameOrId || it.id == networkNameOrId } return network } + /** + * Gets networks based on name or id, note there might be multiple networks with the same name + * @param networkNameOrIds + * @return Networks if found, null if not + */ + ArrayList getDockerNetworks(ArrayList networkNameOrIds) { + + ArrayList networks = networkClient.networks().content.findAll { it.name in networkNameOrIds || it.id in networkNameOrIds } + + return networks + } boolean networkIsValid(Network network) { - return getNetwork(network.id) != null + return getDockerNetwork(network.id) != null } /** * Get the networks that this container is connected too * @return */ - ArrayList getContainerNetworks() { + ArrayList getConnectedContainerNetworks() { Map rawResponse = inspectContainer().networkSettings.networks ArrayList networks = [] rawResponse.keySet().each { networkId -> - Network network = getNetwork(networkId) + Network network = getDockerNetwork(networkId) if (network != null) { networks.add(network) @@ -509,7 +517,7 @@ trait Container { ArrayList getContainerBridgeNetworks() { - return getContainerNetworks().findAll { it.driver == "bridge" } + return getConnectedContainerNetworks().findAll { it.driver == "bridge" } } @@ -533,7 +541,7 @@ trait Container { log.trace("\tVerifying container was added to network") - if (containerNetworks.find { it.id == network.id } != null) { + if (connectedContainerNetworks.find { it.id == network.id } != null) { log.info("\tContainer was successfully added to network") return true } @@ -550,7 +558,7 @@ trait Container { networkClient.disconnectNetwork(network.id, containerId) log.trace("\tVerifying container was disconnected from network") - if (!containerNetworks.find { it.id == network.id }) { + if (!connectedContainerNetworks.find { it.id == network.id }) { log.info("\tContainer was successfully disconnected from network") return true } @@ -569,8 +577,7 @@ trait Container { log.info("Setting container networks") log.info("\tBeginning by disconnecting any networks it should no longer be connected to") - ArrayList networks = containerNetworks - containerNetworks.each { connectedNetwork -> + connectedContainerNetworks.each { connectedNetwork -> if (newNetworks.id.find { newNetworkId -> newNetworkId != connectedNetwork.id }) { assert disconnectContainerFromNetwork(connectedNetwork): "Error disconnecting container (${containerName}) from network: ${connectedNetwork.name} (${connectedNetwork.id})" @@ -580,7 +587,7 @@ trait Container { } log.info("\tFinished disconnecting container from unwanted networks, now connecting to new networks") - ArrayList connectedNetworks = containerNetworks + ArrayList connectedNetworks = connectedContainerNetworks newNetworks.each { wantedNetwork -> if (connectedNetworks.id.find { wantedNetwork.id }) { @@ -644,23 +651,45 @@ trait Container { } - ArrayList runBashCommandInContainer(String command, long timeoutS = 10) { + //Format is one of: `user`, `user:group`, `uid`, or `uid:gid` + ArrayList runCommandInContainer(String containerId, ArrayList commands, long timeoutS = 10, String userGroup = null, String workingDir = null) { log.info("Executing bash command in container:") log.info("\tContainer:" + self.containerName + " (${self.containerId})") - log.info("\tCommand:" + command) + log.info("\tCommand:" + commands) log.info("\tTimeout:" + timeoutS) - if (log.isTraceEnabled()) { - log.trace("\tDocker ping:" + dockerClient.ping().content as String) - } - long cmdStart = System.currentTimeMillis() + + ExecConfig execConfig = new ExecConfig() + execConfig.with { ex -> + ex.attachStdin = false + ex.attachStdout = true + ex.attachStderr = true + ex.detachKeys = null + ex.tty = false + ex.env = null + ex.cmd = commands + ex.privileged = null + ex.user = userGroup ?: null + ex.workingDir = workingDir ?: null + } + ContainerCallback callBack = new ContainerCallback() - EngineResponse response = dockerClient.exec(self.containerId, [self.defaultShell, "-c", command], callBack, Duration.ofSeconds(timeoutS)) + dockerClient.exec(containerId, commands, callBack, Duration.ofSeconds(timeoutS), execConfig) + + log.trace("\tCommand finished after:" + ((System.currentTimeMillis() - cmdStart) / 1000).round() + "s") - log.trace("\tCommand finished after:" + ((System.currentTimeMillis()-cmdStart)/1000).round() + "s" ) return callBack.output + + } + + + ArrayList runBashCommandInContainer(String command, long timeoutS = 10, String user = null) { + + return runCommandInContainer(self.containerId, [self.defaultShell, "-c", command], timeoutS, user) + + } diff --git a/src/main/groovy/com/eficode/devstack/container/impl/HarborManagerContainer.groovy b/src/main/groovy/com/eficode/devstack/container/impl/HarborManagerContainer.groovy index b040f3c..8dcffd8 100644 --- a/src/main/groovy/com/eficode/devstack/container/impl/HarborManagerContainer.groovy +++ b/src/main/groovy/com/eficode/devstack/container/impl/HarborManagerContainer.groovy @@ -6,6 +6,7 @@ import org.apache.groovy.json.internal.LazyMap import java.nio.file.Files import java.nio.file.Path +import java.util.Map.Entry class HarborManagerContainer extends DoodContainer { @@ -78,68 +79,141 @@ class HarborManagerContainer extends DoodContainer { cmdOutput = runBashCommandInContainer(""" - mkdir -p "${installPath}" && \\ + mkdir -p "${installPath}" && \\ cd "${installPath}" && \\ wget https://github.com/goharbor/harbor/releases/download/$harborVersion/harbor-online-installer-${harborVersion}.tgz && \\ tar xzvf harbor-online-installer-${harborVersion}.tgz && \\ cd harbor && \\ ls -la && \\ echo status: \$? - """, 100) + """, 200) assert cmdOutput.last() == "status: 0": "Error downloading and extracting harbor:" + cmdOutput.join("\n") log.info("\tFinished downloading and extracting Harbor") - /** - * Fetch template config file, update it and put it back in the manager container - */ + assert modifyInstallYml() : "Error updating Harbor install config file: harbor.yml" - Path tmpDir= Files.createTempDirectory("harbor-conf") + log.info("\tStarting installation") + cmdOutput = runBashCommandInContainer(installPath + "/harbor/install.sh ; echo status: \$?", 400 ) + assert cmdOutput.last().contains("status: 0"): "Error installing harbor:" + cmdOutput.join("\n") + + + assert modifyDockerCompose() : "Error modifying Harbors docker-compose file" + + cmdOutput = runBashCommandInContainer("cd " + installPath + "/harbor && docker-compose up -d && echo status: \$?", 80) + assert cmdOutput.last().contains("status: 0"): "Error applying the modified docker-compose file:" + cmdOutput.join("\n") + + return true + + } + + + /** + * Modifies the default docker-compose file so that all pods connect to the correct network (containerDefaultNetworks) + * @return + */ + boolean modifyDockerCompose() { + + + + + log.info("\tCustomizing Harbor docker compose") + Path tmpDir= Files.createTempDirectory("harbor-compose") String tmpDirPath = tmpDir.toFile().absolutePath - ArrayList files = copyFilesFromContainer("${installPath}/harbor/harbor.yml.tmpl", tmpDirPath + "/") - assert files.size() == 1 && files.first().name == "harbor.yml.tmpl" : "Error, could not find template config file" + ArrayList files = copyFilesFromContainer("${installPath}/harbor/docker-compose.yml", tmpDirPath + "/") + + assert files.size() == 1 && files.first().name == "docker-compose.yml" : "Error, could not find docker-compose.yml file" File yamlFile = files.first() + log.debug("\t\tRetried docker compose file from container:" + yamlFile.absolutePath) + + + LazyMap originalYml = new YamlSlurper().parse(yamlFile) as LazyMap + LazyMap modifiedYml = new YamlSlurper().parse(yamlFile) as LazyMap + + + + modifiedYml.services.each {Entry service -> + log.debug("\t"*3 + "Customising Docker Service:" + service.key) + + service.value.networks = containerDefaultNetworks + log.trace("\t"*4 + "Set networks to:" + service.value.networks) + log.trace("\t"*4 + "Used to be:" + originalYml.services.get(service.key).networks) + + } + + log.debug("\t"*3 + "Customising Docker Network") + modifiedYml.remove("networks") + + Map networks = [:] + containerDefaultNetworks.each {networkName -> + networks.put(networkName as String,["external": true, "name":networkName] as LazyMap ) + } + modifiedYml.put("networks", networks) + //modifiedYml.put("networks", ["default": [external: [name: networkName]]]) + + log.trace("\t"*4 + "Set networks to:" + modifiedYml.networks) + log.trace("\t"*4 + "Used to be:" + originalYml.networks) - LazyMap yaml = new YamlSlurper().parse(yamlFile) as LazyMap - LazyMap modifiedYaml = modifyHarborYml(yaml) YamlBuilder yamlBuilder = new YamlBuilder() - yamlBuilder(modifiedYaml) + yamlBuilder(modifiedYml) - File modifiedYamlFile = new File(tmpDirPath + "/harbor.yml") - modifiedYamlFile.createNewFile() - modifiedYamlFile.write(yamlBuilder.toString().replaceAll("\"", "")) - assert copyFileToContainer(modifiedYamlFile.absolutePath, installPath + "/harbor/") : "Error copying updated YAML file to container" + yamlFile.write(yamlBuilder.toString()) + + assert copyFileToContainer(yamlFile.absolutePath, installPath + "/harbor/") : "Error copying updated YAML file to container" tmpDir.deleteDir() log.info("\tFinished customizing installation configuration") - log.info("\tStarting installation") - cmdOutput = runBashCommandInContainer(installPath + "/harbor/install.sh ; echo status: \$?", 400 ) - assert cmdOutput.last().contains("status: 0"): "Error installing harbor:" + cmdOutput.join("\n") + return true + + } + /** + * Fetch template config file, update it and put it back in the manager container + */ + boolean modifyInstallYml() { - return true + Path tmpDir= Files.createTempDirectory("harbor-conf") + String tmpDirPath = tmpDir.toFile().absolutePath - } - LazyMap modifyHarborYml(LazyMap originalYml) { + ArrayList files = copyFilesFromContainer("${installPath}/harbor/harbor.yml.tmpl", tmpDirPath + "/") - LazyMap modifiedYml = originalYml + assert files.size() == 1 && files.first().name == "harbor.yml.tmpl" : "Error, could not find template config file" + File yamlFile = files.first() + + + LazyMap originalYml = new YamlSlurper().parse(yamlFile) as LazyMap + + LazyMap modifiedYml = originalYml modifiedYml.hostname = host modifiedYml.http.port = port modifiedYml.remove("https") modifiedYml.data_volume = dataPath - return modifiedYml + + YamlBuilder yamlBuilder = new YamlBuilder() + yamlBuilder(modifiedYml) + + File modifiedYamlFile = new File(tmpDirPath + "/harbor.yml") + modifiedYamlFile.createNewFile() + modifiedYamlFile.write(yamlBuilder.toString().replaceAll("\"", "")) + + assert copyFileToContainer(modifiedYamlFile.absolutePath, installPath + "/harbor/") : "Error copying updated YAML file to container" + tmpDir.deleteDir() + + log.info("\tFinished customizing installation configuration") + + return true } } diff --git a/src/main/groovy/com/eficode/devstack/deployment/Deployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/Deployment.groovy index ff331da..ce30e14 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/Deployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/Deployment.groovy @@ -1,12 +1,15 @@ package com.eficode.devstack.deployment import com.eficode.devstack.container.Container +import de.gesellix.docker.remote.api.core.Frame +import de.gesellix.docker.remote.api.core.StreamCallback import org.slf4j.Logger import org.slf4j.LoggerFactory trait Deployment { - static Logger log = LoggerFactory.getLogger(this.class) + + Logger log = LoggerFactory.getLogger(this.class) abstract ArrayList containers abstract String friendlyName String deploymentNetworkName = "bridge" @@ -14,23 +17,6 @@ trait Deployment { abstract boolean setupDeployment() - /* - void setupSecureDockerConnection(String host, String certPath) { - - log.info("Setting up secure connection to docker engine") - assert getContainers() != null && !getContainers().empty: "Deployment has no containers defined" - - - getContainers().each { - assert it.setupSecureRemoteConnection(host, certPath): "Error setting up secure connection to docker engine" - log.info("\tSecure connection setup for container:" + getFriendlyName()) - } - log.info("\tSuccessfully setup secure connections to docker engine") - } - - */ - - boolean startDeployment() { log.info("Starting deployment: " + this.friendlyName) diff --git a/src/main/groovy/com/eficode/devstack/deployment/impl/BitbucketH2Deployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/impl/BitbucketH2Deployment.groovy index e0667d6..f948d56 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/impl/BitbucketH2Deployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/impl/BitbucketH2Deployment.groovy @@ -4,9 +4,12 @@ import com.eficode.atlassian.bitbucketInstanceManager.BitbucketInstanceManagerRe import com.eficode.devstack.container.Container import com.eficode.devstack.container.impl.BitbucketContainer import com.eficode.devstack.deployment.Deployment +import org.slf4j.Logger +import org.slf4j.LoggerFactory -class BitbucketH2Deployment implements Deployment{ +class BitbucketH2Deployment implements Deployment { + Logger log = LoggerFactory.getLogger(this.class) String friendlyName = "Bitbucket H2 Deployment" BitbucketInstanceManagerRest bitbucketRest ArrayList containers = [] @@ -25,7 +28,7 @@ class BitbucketH2Deployment implements Deployment{ } BitbucketContainer getBitbucketContainer() { - return containers.find {it instanceof BitbucketContainer} as BitbucketContainer + return containers.find { it instanceof BitbucketContainer } as BitbucketContainer } String getBitbucketContainerId() { @@ -45,21 +48,21 @@ class BitbucketH2Deployment implements Deployment{ log.info("Setting up deployment:" + friendlyName) - assert bitbucketLicense : "Error no Bitbucket License has been setup" + assert bitbucketLicense: "Error no Bitbucket License has been setup" + + bitbucketContainer.containerDefaultNetworks = [this.deploymentNetworkName] bitbucketContainer.createContainer() + log.info("\tCreated Bitbucket container:" + bitbucketContainer.id) - log.info("\tConfiguring container to join network:" + this.deploymentNetworkName) - bitbucketContainer.connectContainerToNetwork(bitbucketContainer.getNetwork(this.deploymentNetworkName)) - assert bitbucketContainer.startContainer() : "Error starting Bitbucket container:" + bitbucketContainer.id + assert bitbucketContainer.startContainer(): "Error starting Bitbucket container:" + bitbucketContainer.id log.info("\tStarted Bitbucket container") - log.info("\tSetting up local H2 database, License, Name etc") - assert bitbucketRest.setApplicationProperties(bitbucketLicense, "Bitbucket", bitbucketBaseUrl) : "Error setting up H2 database for Bitbucket" + assert bitbucketRest.setApplicationProperties(bitbucketLicense, "Bitbucket", bitbucketBaseUrl): "Error setting up H2 database for Bitbucket" log.info("\t\tApplication basics setup successfully") diff --git a/src/main/groovy/com/eficode/devstack/deployment/impl/HarborDeployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/impl/HarborDeployment.groovy index 0c3006f..23493ff 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/impl/HarborDeployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/impl/HarborDeployment.groovy @@ -1,18 +1,24 @@ package com.eficode.devstack.deployment.impl import com.eficode.devstack.container.Container -import com.eficode.devstack.container.impl.DoodContainer import com.eficode.devstack.container.impl.HarborManagerContainer import com.eficode.devstack.deployment.Deployment +import com.eficode.devstack.util.DockerClientDS +import de.gesellix.docker.remote.api.ContainerState import de.gesellix.docker.remote.api.ContainerSummary +import de.gesellix.docker.remote.api.ContainerWaitExitError +import org.slf4j.Logger +import org.slf4j.LoggerFactory -class HarborDeployment implements Deployment{ +class HarborDeployment implements Deployment { + Logger log = LoggerFactory.getLogger(this.class) String friendlyName = "Harbor Deployment" ArrayList containers = [] + String deploymentNetworkName = "harbor" - HarborDeployment(String baseUrl, String harborVersion = "v2.6.0", String baseDir = "/opt/", String dockerHost = "", String dockerCertPath = "") { + HarborDeployment(String baseUrl, String harborVersion = "v2.6.0", String baseDir = "/opt/", String dockerHost = "", String dockerCertPath = "") { HarborManagerContainer managerContainer = new HarborManagerContainer(baseUrl, harborVersion, baseDir, dockerHost, dockerCertPath) @@ -22,14 +28,30 @@ class HarborDeployment implements Deployment{ } HarborManagerContainer getManagerContainer() { - this.containers.find {it instanceof HarborManagerContainer} as HarborManagerContainer + this.containers.find { it instanceof HarborManagerContainer } as HarborManagerContainer } @Override boolean setupDeployment() { - managerContainer.createContainer([],["tail", "-f", "/dev/null"]) - managerContainer.startContainer() + + managerContainer.containerDefaultNetworks = [deploymentNetworkName] + assert managerContainer.createContainer([], ["tail", "-f", "/dev/null"]): "Error creating container" + assert managerContainer.startContainer(): "Error starting container" + + sleep(1000) + long start = System.currentTimeMillis() + while (harborContainers.state.find { it != ContainerState.Status.Running.value }) { + + log.info("\tWaiting for containers to start") + harborContainers.collectEntries { [it.names.first(), it.state] }.each { log.debug("\t\t" + it) } + + assert start + (4 * 60000) > System.currentTimeMillis(): "Timed out waiting for harbor containers to start, waited:" + ((System.currentTimeMillis() - start)/1000).round().toString() + sleep(1500) + } + + return true + } @Override @@ -37,17 +59,19 @@ class HarborDeployment implements Deployment{ assert managerContainer.startContainer() sleep(5000) - ArrayList cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.installPath}/harbor ; docker-compose start ; echo status: \$?", 120 ) + ArrayList cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.installPath}/harbor ; docker-compose start ; echo status: \$?", 120) assert cmdOutput.last() == "status: 0": "Error starting harbor:" + cmdOutput.join("\n") + return true } @Override boolean stopDeployment() { + assert managerContainer.startContainer() - ArrayList cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.installPath}/harbor ; docker-compose stop ; echo status: \$?", 120 ) + ArrayList cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.installPath}/harbor ; docker-compose stop ; echo status: \$?", 120) assert cmdOutput.last() == "status: 0": "Error stopping harbor:" + cmdOutput.join("\n") assert managerContainer.stopContainer() @@ -55,38 +79,102 @@ class HarborDeployment implements Deployment{ } @Override - boolean stopAndRemoveDeployment(){ + boolean stopAndRemoveDeployment() { + + log.info("Stopping and removing ${this.class}") + + + assert managerContainer.stopAndRemoveContainer() + ArrayList harbContainers = harborContainers + if (harbContainers) { + //There are still harbor containers around + + DockerClientDS dockerClient = managerContainer.dockerClient + harbContainers.each { container -> + + log.debug("\t\tKilling and removing:" + container.names) + try { + dockerClient.kill(container.id) + } catch (ignored){} + + dockerClient.rm(container.id) + + + + } + + } + + + + + + + return harborContainers.isEmpty() + + /* + if (!managerContainer.created) { + //Manager container does not exist + ArrayList harbContainers = harborContainers + if (harbContainers) { + //There are still harbor containers around + log.info("\tThe manager container does not exist, removing harbor containers manually") + + DockerClientDS dockerClient = managerContainer.dockerClient + harbContainers.each { container -> + + log.debug("\t\tKilling and removing:" + container.names) + try { + dockerClient.kill(container.id) + } catch (ignored){} + + dockerClient.rm(container.id) + + + + } + + } + + return true + + + } + assert managerContainer.startContainer() - ArrayList cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.installPath}/harbor ; docker-compose down ; echo status: \$?", 120 ) + ArrayList cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.installPath}/harbor ; docker-compose down ; echo status: \$?", 120) assert cmdOutput.last() == "status: 0": "Error downing harbor:" + cmdOutput.join("\n") - cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.basePath} ; ls -I data -I install | wc -l", 5 ) + cmdOutput = managerContainer.runBashCommandInContainer("cd ${managerContainer.basePath} ; ls -I data -I install | wc -l", 5) if (cmdOutput != ["0"]) { log.warn("\tNot cleaning up base path ${managerContainer.basePath}, found unexpected content") - }else { - cmdOutput = managerContainer.runBashCommandInContainer("rm -rf ${managerContainer.basePath} ; echo status: \$?", 120 ) + } else { + cmdOutput = managerContainer.runBashCommandInContainer("rm -rf ${managerContainer.basePath} ; echo status: \$?", 120) assert cmdOutput.last() == "status: 0": "Error cleaning up base path ${managerContainer.basePath}: " + cmdOutput.join("\n") } return managerContainer.stopAndRemoveContainer() + */ + } + //TODO should return DevStack container objects, and filter more closely /** * Gets all containers running a Harbor image and the harbor manager container. * NOTE: this is a soft check that likely will return false positives * @return */ - ArrayListgetHarborContainers() { + ArrayList getHarborContainers() { ArrayList containers = managerContainer.dockerClient.ps().content - containers = containers.findAll { it.image.startsWith("goharbor") || it.names.first() == managerContainer.containerName} + containers = containers.findAll { it.image.startsWith("goharbor") || it.names.first() == "/" + managerContainer.containerName } return containers diff --git a/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeployment.groovy index 0776550..a92f0d0 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeployment.groovy @@ -4,6 +4,8 @@ import com.eficode.devstack.container.Container import com.eficode.devstack.container.impl.HarborManagerContainer import com.eficode.devstack.container.impl.JenkinsContainer import com.eficode.devstack.deployment.Deployment +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.util.concurrent.Callable import java.util.concurrent.ExecutorService @@ -12,6 +14,7 @@ import java.util.concurrent.Future class JenkinsAndHarborDeployment implements Deployment { + Logger log = LoggerFactory.getLogger(this.class) String friendlyName = "Jenkins and Harbor Deployment" String deploymentNetworkName = "jenkins_and_harbor" ArrayList subDeployments = [] @@ -23,22 +26,9 @@ class JenkinsAndHarborDeployment implements Deployment { new HarborDeployment(harborBaseUrl, "v2.6.1", "/tmp/", dockerHost, dockerCertPath) ] - } - private class SetupDeploymentTask implements Callable { - - Deployment deployment - - SetupDeploymentTask(Deployment deployment) { - this.deployment = deployment - } - @Override - Boolean call() throws Exception { - this.deployment.setupDeployment() - } - } JenkinsDeployment getJenkinsDeployment() { return subDeployments.find { it instanceof JenkinsDeployment } as JenkinsDeployment @@ -64,6 +54,19 @@ class JenkinsAndHarborDeployment implements Deployment { this.containers = containers } + private class SetupDeploymentTask implements Callable { + + Deployment deployment + + SetupDeploymentTask(Deployment deployment) { + this.deployment = deployment + } + + @Override + Boolean call() throws Exception { + this.deployment.setupDeployment() + } + } boolean setupDeployment() { @@ -118,6 +121,15 @@ class JenkinsAndHarborDeployment implements Deployment { } + @Override + boolean stopAndRemoveDeployment() { + + subDeployments.each{ + it.stopAndRemoveDeployment() + } + + } + } diff --git a/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsDeployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsDeployment.groovy index 7af0717..b339085 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsDeployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/impl/JenkinsDeployment.groovy @@ -5,11 +5,14 @@ import com.eficode.devstack.container.impl.JenkinsContainer import com.eficode.devstack.deployment.Deployment import kong.unirest.Unirest import kong.unirest.UnirestInstance +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.util.concurrent.TimeoutException class JenkinsDeployment implements Deployment{ + Logger log = LoggerFactory.getLogger(this.class) String friendlyName = "Jenkins Deployment" ArrayList containers = [] String baseUrl @@ -21,6 +24,8 @@ class JenkinsDeployment implements Deployment{ jenkinsContainer.containerName = JenkinsContainer.extractDomainFromUrl(jenkinsBaseUrl) jenkinsContainer.containerMainPort = JenkinsContainer.extractPortFromUrl(jenkinsBaseUrl) + + } JenkinsContainer getJenkinsContainer() { @@ -30,7 +35,10 @@ class JenkinsDeployment implements Deployment{ @Override boolean setupDeployment() { + + jenkinsContainer.containerDefaultNetworks = [this.deploymentNetworkName] jenkinsContainer.createContainer() + boolean containerSetupSuccess = jenkinsContainer.startContainer() assert containerSetupSuccess : "Error starting Jenkins container" log.info("\tJenkins container has started") diff --git a/src/main/groovy/com/eficode/devstack/deployment/impl/JsmAndBitbucketH2Deployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/impl/JsmAndBitbucketH2Deployment.groovy index 9b5fdc2..f311dd6 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/impl/JsmAndBitbucketH2Deployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/impl/JsmAndBitbucketH2Deployment.groovy @@ -6,6 +6,8 @@ import com.eficode.devstack.container.Container import com.eficode.devstack.container.impl.BitbucketContainer import com.eficode.devstack.container.impl.JsmContainer import com.eficode.devstack.deployment.Deployment +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.util.concurrent.Callable import java.util.concurrent.ExecutorService @@ -14,9 +16,11 @@ import java.util.concurrent.Future class JsmAndBitbucketH2Deployment implements Deployment { + String friendlyName = "JIRA and Bitbucket H2 Deployment" String containerNetworkName = "jsm_and_bitbucket" ArrayList subDeployments = [] + Logger log = LoggerFactory.getLogger(this.class) Map jiraAppsToInstall = [:] String jiraLicense @@ -131,7 +135,7 @@ class JsmAndBitbucketH2Deployment implements Deployment { threadPool.shutdown() - while (!jsmFuture.done && !bitbucketFuture.done) { + while (!jsmFuture.done || !bitbucketFuture.done) { log.info("Waiting for deployments to finish") log.info("\tJSM Finished:" + jsmFuture.done) log.info("\tBitbucket Finished:" + bitbucketFuture.done) @@ -160,11 +164,12 @@ class JsmAndBitbucketH2Deployment implements Deployment { log.info("\t\tFinished installing user defined JIRA Apps") } - if (!jiraAppsToInstall.any { it.key.contains("JiraShortcuts") }) { + if (!jiraAppsToInstall.any { it.key.contains("jiraShortcuts") }) { log.info("\tInstalling JiraShortcuts app") //Needed for setting up applink to bitbucket assert installJiraApps( [ - "https://github.com/eficode/JiraShortcuts/raw/packages/repository/com/eficode/atlassian/JiraShortcuts/2.0-SNAPSHOT-groovy-3.0/JiraShortcuts-2.0-SNAPSHOT-groovy-3.0.jar": "" + "https://github.com/eficode/JiraShortcuts/raw/packages/repository/com/eficode/atlassian/jira/jiraShortcuts/2.0.1-SNAPSHOT-groovy-3.0/jiraShortcuts-2.0.1-SNAPSHOT-groovy-3.0.jar":"" + ] ) : "Error installing JiraShortcuts JIRA apps" log.info("\t\tFinished installing JiraShortcuts JIRA Apps") diff --git a/src/main/groovy/com/eficode/devstack/deployment/impl/JsmH2Deployment.groovy b/src/main/groovy/com/eficode/devstack/deployment/impl/JsmH2Deployment.groovy index 4af0f85..6aa0871 100644 --- a/src/main/groovy/com/eficode/devstack/deployment/impl/JsmH2Deployment.groovy +++ b/src/main/groovy/com/eficode/devstack/deployment/impl/JsmH2Deployment.groovy @@ -4,10 +4,13 @@ import com.eficode.atlassian.jiraInstanceManager.JiraInstanceManagerRest import com.eficode.devstack.container.Container import com.eficode.devstack.container.impl.JsmContainer import com.eficode.devstack.deployment.Deployment +import org.slf4j.Logger +import org.slf4j.LoggerFactory class JsmH2Deployment implements Deployment{ String friendlyName = "JIRA H2 Deployment" + Logger log = LoggerFactory.getLogger(this.class) JiraInstanceManagerRest jiraRest ArrayList containers = [] Map appsToInstall = [:] @@ -76,12 +79,10 @@ class JsmH2Deployment implements Deployment{ assert jiraLicense : "Error no Jira License has been setup" + jsmContainer.containerDefaultNetworks = [deploymentNetworkName] jsmContainer.createContainer() log.info("\tCreated jsm container:" + jsmContainer.id) - log.info("\tConfiguring container to join network:" + this.deploymentNetworkName) - jsmContainer.connectContainerToNetwork(jsmContainer.getNetwork(this.deploymentNetworkName)) - assert jsmContainer.startContainer() : "Error starting JSM container:" + jsmContainer.id log.info("\tStarted JSM container") diff --git a/src/main/groovy/com/eficode/devstack/util/DockerClientDS.groovy b/src/main/groovy/com/eficode/devstack/util/DockerClientDS.groovy new file mode 100644 index 0000000..75bac8a --- /dev/null +++ b/src/main/groovy/com/eficode/devstack/util/DockerClientDS.groovy @@ -0,0 +1,60 @@ +package com.eficode.devstack.util + +import com.eficode.devstack.util.DockerClientDS +import de.gesellix.docker.client.DockerClientImpl +import de.gesellix.docker.client.EngineResponseContent +import de.gesellix.docker.engine.DockerClientConfig +import de.gesellix.docker.engine.DockerEnv +import de.gesellix.docker.remote.api.ExecConfig +import de.gesellix.docker.remote.api.ExecStartConfig +import de.gesellix.docker.remote.api.IdResponse +import de.gesellix.docker.remote.api.core.Frame +import de.gesellix.docker.remote.api.core.StreamCallback +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.time.Duration + +import static java.net.Proxy.NO_PROXY + +class DockerClientDS extends DockerClientImpl { + + private final Logger log = LoggerFactory.getLogger(DockerClientDS) + + + DockerClientDS() { + super() + } + + DockerClientDS(String dockerHost) { + super(dockerHost) + } + + DockerClientDS(DockerEnv env, Proxy proxy = NO_PROXY) { + super(env, proxy) + } + + DockerClientDS(DockerClientConfig dockerClientConfig, Proxy proxy = NO_PROXY) { + super(dockerClientConfig, proxy) + } + + + + EngineResponseContent exec(String containerId, List command, StreamCallback callback, Duration timeout, ExecConfig execConfig) { + + log.info("docker exec '${containerId}' '${command}'") + + + + EngineResponseContent execCreateResult = createExec(containerId, execConfig) + String execId = execCreateResult.content.id + ExecStartConfig execStartConfig = new ExecStartConfig( + (execConfig.detachKeys ?: false) as Boolean, + execConfig.tty + ) + startExec(execId, execStartConfig, callback, timeout) + return execCreateResult + + } + +} diff --git a/src/main/resources/com/eficode/devstack/deployment/jira/scripts/CreateBitbucketLink.groovy b/src/main/resources/com/eficode/devstack/deployment/jira/scripts/CreateBitbucketLink.groovy index 689665f..96d0870 100644 --- a/src/main/resources/com/eficode/devstack/deployment/jira/scripts/CreateBitbucketLink.groovy +++ b/src/main/resources/com/eficode/devstack/deployment/jira/scripts/CreateBitbucketLink.groovy @@ -17,8 +17,8 @@ import com.atlassian.applinks.api.ApplicationLink import com.atlassian.applinks.api.application.bitbucket.BitbucketApplicationType import com.onresolve.scriptrunner.runner.customisers.WithPlugin -@WithPlugin("com.eficode.atlassian.JiraShortcuts") -import com.eficode.atlassian.JiraShortcuts.JiraShortcuts +@WithPlugin("com.eficode.atlassian.jira.jiraShortcuts") +import com.eficode.atlassian.jira.jiraShortcuts.JiraShortcuts import org.apache.log4j.Level import org.apache.log4j.Logger diff --git a/src/test/groovy/com/eficode/devstack/DevStackSpec.groovy b/src/test/groovy/com/eficode/devstack/DevStackSpec.groovy index 400cc6e..a611869 100644 --- a/src/test/groovy/com/eficode/devstack/DevStackSpec.groovy +++ b/src/test/groovy/com/eficode/devstack/DevStackSpec.groovy @@ -1,12 +1,13 @@ package com.eficode.devstack - -import de.gesellix.docker.client.DockerClientImpl +import com.eficode.devstack.container.impl.AlpineContainer +import com.eficode.devstack.util.DockerClientDS import de.gesellix.docker.engine.DockerClientConfig import de.gesellix.docker.engine.DockerEnv import de.gesellix.docker.remote.api.ContainerInspectResponse import de.gesellix.docker.remote.api.ContainerState import de.gesellix.docker.remote.api.ContainerSummary +import de.gesellix.docker.remote.api.Network import org.apache.commons.io.FileUtils import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -21,13 +22,16 @@ class DevStackSpec extends Specification { String dockerCertPath = "resources/dockerCert" @Shared - DockerClientImpl dockerClient + DockerClientDS dockerClient @Shared ArrayList cleanupContainerNames @Shared ArrayList cleanupContainerPorts + @Shared + ArrayList cleanupDockerNetworkNames = [] + @Shared boolean disableCleanup = false @@ -42,6 +46,7 @@ class DevStackSpec extends Specification { dockerClient = resolveDockerClient() if (!disableCleanup) { cleanupContainers() + cleanupNetworks() } } @@ -50,17 +55,28 @@ class DevStackSpec extends Specification { def cleanup() { if (!disableCleanup) { cleanupContainers() + cleanupNetworks() } } + boolean cleanupNetworks() { + + AlpineContainer alp = new AlpineContainer(dockerRemoteHost, dockerCertPath) + cleanupDockerNetworkNames.each {networkName -> + Network network = alp.getDockerNetwork(networkName) + log.info("\tRemoving network ${network.name} " + network?.id[0..7]) + assert alp.removeNetwork(network) : "Error removing network:" + network.toString() + } + + } boolean cleanupContainers() { - DockerClientImpl dockerClient = resolveDockerClient() + DockerClientDS dockerClient = resolveDockerClient() log.info("Cleaning up containers") ArrayList containers = dockerClient.ps().content.collect {dockerClient.inspectContainer(it.id as String).content} @@ -97,7 +113,7 @@ class DevStackSpec extends Specification { } - DockerClientImpl resolveDockerClient() { + DockerClientDS resolveDockerClient() { log.info("Resolving Docker client") @@ -134,7 +150,7 @@ class DevStackSpec extends Specification { if (!dockerHost) { log.info("\tNo remote host configured, returning local docker connection") - return new DockerClientImpl() + return new DockerClientDS() } @@ -146,7 +162,7 @@ class DevStackSpec extends Specification { if (!pemFiles.empty && ["ca.pem", "cert.pem", "key.pem"].every { expectedFile -> pemFiles.any { actualFile -> actualFile.name == expectedFile } }) { log.info("\tFound Docker certs, returning Secure remote Docker connection") try { - DockerClientImpl dockerClient = setupSecureRemoteConnection(dockerRemoteHost, dockerCertPath) + DockerClientDS dockerClient = setupSecureRemoteConnection(dockerRemoteHost, dockerCertPath) assert dockerClient.ping().content as String == "OK": "Error pinging remote Docker engine" return dockerClient } catch (ex) { @@ -167,7 +183,7 @@ class DevStackSpec extends Specification { * @param host ex: "https://docker.domain.se:2376" * @param certPath folder containing ca.pem, cert.pem, key.pem */ - static DockerClientImpl setupSecureRemoteConnection(String host, String certPath) { + static DockerClientDS setupSecureRemoteConnection(String host, String certPath) { DockerClientConfig dockerConfig = new DockerClientConfig(host) DockerEnv dockerEnv = new DockerEnv(host) @@ -175,7 +191,7 @@ class DevStackSpec extends Specification { dockerEnv.setTlsVerify("1") dockerConfig.apply(dockerEnv) - return new DockerClientImpl(dockerConfig) + return new DockerClientDS(dockerConfig) } diff --git a/src/test/groovy/com/eficode/devstack/container/ContainerTest.groovy b/src/test/groovy/com/eficode/devstack/container/ContainerTest.groovy index d3ee3ad..3c4de21 100644 --- a/src/test/groovy/com/eficode/devstack/container/ContainerTest.groovy +++ b/src/test/groovy/com/eficode/devstack/container/ContainerTest.groovy @@ -29,6 +29,28 @@ class ContainerTest extends DevStackSpec { } + def testRunBashCommand(String dockerHost, String certPath) { + + setup: + AlpineContainer alpine1 = new AlpineContainer(dockerHost, certPath) + alpine1.containerName = "spock-alpine1" + alpine1.createSleepyContainer() + alpine1.startContainer() + + expect: "Test that the user parameter of runBashCommandInContainer works" + alpine1.runBashCommandInContainer("whoami") == ["root"] + alpine1.runBashCommandInContainer("adduser -D nisse && su nisse -c whoami") == ["nisse"] + alpine1.runBashCommandInContainer("whoami", 10, "nisse") == ["nisse"] + + + where: + dockerHost | certPath + "" | "" + dockerRemoteHost | dockerCertPath + + } + + def testNetworking(String dockerHost, String certPath) { setup: @@ -93,8 +115,8 @@ class ContainerTest extends DevStackSpec { alpine1.removeNetwork(removedNetwork) - alpine1.getNetwork(removedNetwork.id) == null - alpine1.getNetwork(removedNetwork.name) == null + alpine1.getDockerNetwork(removedNetwork.id) == null + alpine1.getDockerNetwork(removedNetwork.name) == null log.info("\tRemoval of networks was tested successfully") @@ -135,8 +157,8 @@ class ContainerTest extends DevStackSpec { then: "They should both be able to ping each other using containerName and ip" - alpine1.getContainerNetworks() == [spockNetwork] - alpine2.getContainerNetworks() == [spockNetwork] + alpine1.getConnectedContainerNetworks() == [spockNetwork] + alpine2.getConnectedContainerNetworks() == [spockNetwork] alpine1.runBashCommandInContainer("ping -c 1 " + alpine2.containerName).any { it.contains("0% packet loss") } alpine2.runBashCommandInContainer("ping -c 1 " + alpine1.containerName).any { it.contains("0% packet loss") } alpine1.runBashCommandInContainer("ping -c 1 " + alpine2.ips.first()).any { it.contains("0% packet loss") } diff --git a/src/test/groovy/com/eficode/devstack/deployment/impl/BitbucketH2DeploymentTest.groovy b/src/test/groovy/com/eficode/devstack/deployment/impl/BitbucketH2DeploymentTest.groovy index a16d6e8..124922d 100644 --- a/src/test/groovy/com/eficode/devstack/deployment/impl/BitbucketH2DeploymentTest.groovy +++ b/src/test/groovy/com/eficode/devstack/deployment/impl/BitbucketH2DeploymentTest.groovy @@ -1,7 +1,7 @@ package com.eficode.devstack.deployment.impl import com.eficode.devstack.DevStackSpec -import de.gesellix.docker.client.DockerClientImpl +import com.eficode.devstack.util.DockerClientDS import de.gesellix.docker.engine.DockerClientConfig import de.gesellix.docker.engine.DockerEnv import kong.unirest.Unirest diff --git a/src/test/groovy/com/eficode/devstack/deployment/impl/HarborDeploymentTest.groovy b/src/test/groovy/com/eficode/devstack/deployment/impl/HarborDeploymentTest.groovy index 3ccf195..1ffdadc 100644 --- a/src/test/groovy/com/eficode/devstack/deployment/impl/HarborDeploymentTest.groovy +++ b/src/test/groovy/com/eficode/devstack/deployment/impl/HarborDeploymentTest.groovy @@ -49,16 +49,24 @@ class HarborDeploymentTest extends DevStackSpec { hd.setupDeployment() + //hd.managerContainer.runAfterDockerSetup() + then: Unirest.get(harborBaseUrl).basicAuth("admin", "Harbor12345").asEmpty().status == 200 - hd.getContainers().every { it.status() == ContainerState.Status.Running } + hd.harborContainers.id.contains(hd.managerContainer.inspectContainer().id) //Make sure harborContainers returns manager container as well + hd.harborContainers.every { it.state in [ContainerState.Status.Running.value, ContainerState.Status.Restarting.value] } + hd.harborContainers.every {it.networkSettings.networks.keySet().toList() == [hd.deploymentNetworkName]} + hd.harborContainers.collect {it.names.first()}.every {containerName -> + String hostname = containerName[1..-1] + hd.managerContainer.runBashCommandInContainer("ping ${hostname} -c 1 && echo Status: \$?", 5).last().contains("Status: 0") + } when: "Stopping the deployment" hd.stopDeployment() then: "All containers should remain but have status Exited" - hd.getContainers().every { it.status() == ContainerState.Status.Exited } + hd.harborContainers.every { it.state == ContainerState.Status.Exited.value } when: "Staring the deployment" ArrayList containerIdsBeforeStart = hd.getHarborContainers().id @@ -79,7 +87,7 @@ class HarborDeploymentTest extends DevStackSpec { where: dockerHost | certPath | harborBaseUrl | harborVersion | harborBaseDir "" | "" | "http://localhost" | "v2.6.0" | "/tmp" - dockerRemoteHost | dockerCertPath | "http://harbor.domain.se" | "v2.6.0" | "/tmp" + //dockerRemoteHost | dockerCertPath | "http://harbor.domain.se" | "v2.6.0" | "/tmp" } } diff --git a/src/test/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeploymentTest.groovy b/src/test/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeploymentTest.groovy index ce1c9eb..556b804 100644 --- a/src/test/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeploymentTest.groovy +++ b/src/test/groovy/com/eficode/devstack/deployment/impl/JenkinsAndHarborDeploymentTest.groovy @@ -1,9 +1,9 @@ -package com.eficode.devstack.deployment.impl; +package com.eficode.devstack.deployment.impl import com.eficode.devstack.DevStackSpec import org.slf4j.LoggerFactory -public class JenkinsAndHarborDeploymentTest extends DevStackSpec { +class JenkinsAndHarborDeploymentTest extends DevStackSpec { def setupSpec() { @@ -37,11 +37,23 @@ public class JenkinsAndHarborDeploymentTest extends DevStackSpec { def "test setupDeployment"(String jenkinsBaseUrl, String harborBaseUrl, String dockerHost, String certPath) { setup: + String networkName = "custom-network-" + System.currentTimeMillis().toString()[-5..-1] + cleanupDockerNetworkNames.add(networkName) + + JenkinsAndHarborDeployment jh = new JenkinsAndHarborDeployment(jenkinsBaseUrl, harborBaseUrl, dockerHost, certPath) + jh.deploymentNetworkName = networkName expect: jh.setupDeployment() + when: "Collecting all networks used by harbor and jenkins containers" + ArrayList harborNetworks = jh.harborDeployment.harborContainers.networkSettings.networks.collect {it.keySet()}.flatten().unique() + ArrayList jenkinsNetworks = jh.jenkinsContainer.connectedContainerNetworks.name.unique() + + then: "They should all be in the same networks" + harborNetworks == jenkinsNetworks + where: jenkinsBaseUrl | harborBaseUrl | dockerHost | certPath "http://jenkins.domain.se:8080" | "http://harbor.domain.se" | dockerRemoteHost | dockerCertPath