Skip to content

Commit

Permalink
2.3.21-SNAPSHOT (#42)
Browse files Browse the repository at this point in the history
* DirectorySyncer.groovy
 * Now returns existing matching DirectorySyncer-container if it exists. Intended to stop creation of duplicates

* Bumped to 2.3.20

* Container.groovy
 * Switched IOUtils package
pom.xml
 * Bumped commons-compress to 1.26.0 to work better with dockerclient lib
  * 1.26.1 appears to have a bug when extracting

* ImageSummaryDS.groovy
 * Initial commit of file

* ImageSummaryDS.groovy
 * Before updating replaceDockerUser to use prependStartupScript

* ImageSummaryDS.groovy/replaceDockerUser()
 * Tried to switch to changing permissions on container boot,
  but that would mean that container has to start always start as root and then drop to other user,
  this grew to complex, among other things PATH/ENVs is then no longer inherited.

* AbstractContainer.groovy
 * A helper class intended to let you create arbitrary containers

AllureContainer.groovy
 * WIP

JsmDevDeployment.groovy
 * A deployment for users who wish to develop for JSM

JsmContainer.groovy
 * Fixed bug in isAppAppUploadEnabled()

Container.groovy
 * Added new field "user" for setting docker user on creation
 * Helper methods for env
  * prepareCustomEnvVar, hasEnv

 DockerClientDS.groovy
  * Added simplified build command build()

 ImageSummaryDS.groovy
  * Improvements to replaceDockerUser()

* ImageSummaryDS.groovy
 * Cleanup
pom.xml
 * Bumped to 2.3.21-SNAPSHOT
  • Loading branch information
farthinder authored May 10, 2024
1 parent 279be1b commit 9e12884
Show file tree
Hide file tree
Showing 9 changed files with 775 additions and 24 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.eficode</groupId>
<artifactId>devstack</artifactId>
<version>2.3.20</version>
<version>2.3.21-SNAPSHOT</version>
<packaging>jar</packaging>

<name>DevStack</name>
Expand Down Expand Up @@ -87,7 +87,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.21</version>
<version>1.26.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
Expand Down
54 changes: 39 additions & 15 deletions src/main/groovy/com/eficode/devstack/container/Container.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.tar.TarArchiveEntry
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
import org.apache.commons.compress.utils.IOUtils
import org.apache.commons.io.IOUtils
import org.apache.commons.io.FileUtils
import org.codehaus.groovy.runtime.ResourceGroovyMethods
import org.slf4j.Logger
Expand All @@ -51,6 +51,7 @@ trait Container {
abstract String containerImageTag
ArrayList<String> containerDefaultNetworks = ["bridge"]
ArrayList<String> customEnvVar = []
String user = null //The user name (or UID) and optionally the user group (or GID) to use as the default user

String defaultShell = "/bin/bash"
String containerId
Expand Down Expand Up @@ -148,7 +149,7 @@ trait Container {

/**
* This capability will be added to containers when they are being created
* @param capability, ex: SYS_ADMIN
* @param capability , ex: SYS_ADMIN
*/
void prepareCapability(String capability) {

Expand Down Expand Up @@ -177,6 +178,39 @@ trait Container {
}


/**
* Prepare custom environmental variables. Must be set before creating container
* @param keyVar Ex: ["key=value", "PATH=/user/local/sbin"]
*/
void prepareCustomEnvVar(ArrayList<String> keyVar) {

if (!created) {
self.customEnvVar.addAll(keyVar.collect { it.toString() })
} else if (!keyVar.every { hasEnv(it) }) {
throw new InputMismatchException("Error, cant set new custom environment variables after creating container")
}

}

/**
* Checks if container has been created, and has an environmental var applied
* @param envKeyValue , ex "PATH=/user/local/sbin"
* @return true if present
*/
boolean hasEnv(String envKeyValue) {

if (!created) {
return false
}

List<String> envs = inspectContainer()?.getConfig()?.env
Boolean present = envs?.toString()?.contains(envKeyValue) ?: false
return present


}


/**
* Get MountPoints currently attached to container
* @return
Expand All @@ -197,6 +231,9 @@ trait Container {
c.exposedPorts = [(self.containerMainPort + "/tcp"): [:]]
}

if (self.user) {
c.user = self.user
}

c.hostConfig = new HostConfig().tap { h ->
if (self.containerMainPort) {
Expand Down Expand Up @@ -872,8 +909,6 @@ trait Container {
}
}


//Format is one of: `user`, `user:group`, `uid`, or `uid:gid`
ArrayList<String> runCommandInContainer(String containerId, ArrayList<String> commands, long timeoutS = 10, String userGroup = null, String workingDir = null) {

log.info("Executing bash command in container:")
Expand Down Expand Up @@ -1051,17 +1086,6 @@ trait Container {
return out
}

/**
* Prepare custom environmental variables. Must be set before creating container
* @param keyVar Ex: ["key=value", "PATH=/user/local/sbin"]
*/
void prepareCustomEnvVar(ArrayList<String> keyVar) {

assert hasNeverBeenStarted(): "Error, cant set custom environment variables after creating container"

self.customEnvVar.addAll(keyVar.collect {it.toString()})
}


ArrayList<String> getContainerLogs() {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.eficode.devstack.container.impl

import com.eficode.devstack.container.Container

/**
* A helper class intended to let you create arbitrary containers quickly
*/
class AbstractContainer implements Container {

String containerName
String containerMainPort = null
String containerImage
String containerImageTag
String defaultShell


AbstractContainer(String containerName, String containerMainPort, String containerImage, String containerImageTag, String defaultShell) {
this.containerName = containerName
this.containerMainPort = containerMainPort
this.containerImage = containerImage
this.containerImageTag = containerImageTag
this.defaultShell = defaultShell
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.eficode.devstack.container.impl

import com.eficode.devstack.container.Container
import de.gesellix.docker.remote.api.ContainerCreateRequest

class AllureContainer implements Container {

String containerName = "Alpine"
String containerMainPort = "5050"
String containerImage = "frankescobar/allure-docker-service"
String containerImageTag = "latest"
String defaultShell = "/bin/bash"
String user = "2001:2001"


AllureContainer(String dockerHost = "", String dockerCertPath = "") {
if (dockerHost && dockerCertPath) {
assert setupSecureRemoteConnection(dockerHost, dockerCertPath): "Error setting up secure remote docker connection"
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class JsmContainer implements Container {
log.info("Enabling upload of Custom JIRA Apps")
if (appAppUploadEnabled) {
log.debug("\tApp upload is already enabled")
return
}
assert !created: "Error, cant enable App Upload for a container that has already been created"
jvmSupportRecommendedArgs += ["-Dupm.plugin.upload.enabled=true"]
Expand All @@ -63,12 +64,7 @@ class JsmContainer implements Container {
* @return
*/
boolean isAppAppUploadEnabled() {

if (!created) {
return false
}
return inspectContainer()?.getConfig()?.env?.toString()?.contains("-Dupm.plugin.upload.enabled=true") ?: false

hasEnv("-Dupm.plugin.upload.enabled=true")
}

/**
Expand Down Expand Up @@ -140,7 +136,6 @@ class JsmContainer implements Container {
}



return containerCreateRequest

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package com.eficode.devstack.deployment.impl

import com.eficode.devstack.container.Container
import com.eficode.devstack.container.impl.AllureContainer
import com.eficode.devstack.container.impl.JsmContainer
import com.eficode.devstack.deployment.Deployment
import com.eficode.devstack.util.DirectorySyncer
import com.eficode.devstack.util.DockerClientDS
import de.gesellix.docker.client.DockerClient
import de.gesellix.docker.client.EngineResponseContent
import de.gesellix.docker.remote.api.Volume
import org.slf4j.Logger
import org.slf4j.LoggerFactory


class JsmDevDeployment implements Deployment {

Logger log = LoggerFactory.getLogger(this.class)
String friendlyName = "JSM Development Platform"
String deploymentNetworkName = "jsmdev"
ArrayList<Deployment> subDeployments = []

DirectorySyncer srcSyncer
ArrayList<String> srcCodePaths
Volume srcCodeVolume

AllureContainer allureContainer
Volume allureReportVolume

DockerClientDS dockerClient

//Used when naming various Docker components
String componentsPrefix

JsmDevDeployment(String jiraBaseUrl, String dockerHost, String dockerCertPath, ArrayList<String> srcCodePaths) {

componentsPrefix = AllureContainer.extractDomainFromUrl(jiraBaseUrl)

allureContainer = new AllureContainer(dockerHost, dockerCertPath)
allureContainer.containerName = componentsPrefix + "-reporter"
dockerClient = allureContainer.dockerClient

allureReportVolume = getOrCreateVolume( componentsPrefix+ "-reports")
allureContainer.prepareCustomEnvVar(["CHECK_RESULTS_EVERY_SECONDS=3", "KEEP_HISTORY=1", "KEEP_HISTORY_LATEST=30"])
allureContainer.prepareVolumeMount(allureReportVolume.name, "/app/allure-results", false)


srcCodeVolume = getOrCreateVolume(componentsPrefix + "-code")
this.srcCodePaths = srcCodePaths

subDeployments.add(new JsmH2Deployment(jiraBaseUrl, dockerHost, dockerCertPath))
jsmDeployment.jsmContainer.prepareVolumeMount(srcCodeVolume.name, "/var/atlassian/application-data/jira/scripts/", false)
jsmDeployment.jsmContainer.prepareVolumeMount(allureReportVolume.name, "/var/atlassian/application-data/jira/allure-results/", false)


}

JsmH2Deployment getJsmDeployment() {
return subDeployments.find { it instanceof JsmH2Deployment } as JsmH2Deployment
}

JsmContainer getJsmContainer() {
return jsmDeployment.jsmContainer
}


@Override
ArrayList<Container> getContainers() {
return [srcSyncer, allureContainer, jsmContainer]
}

@Override
void setContainers(ArrayList<Container> containers) {
throw new InputMismatchException("Not implemented")
}

Volume getOrCreateVolume(String volumeName) {
Volume volume = dockerClient.getVolumesWithName(volumeName).find { true }

if (volume) {
log.debug("\tFound existing volume:" + volume.name)
} else {
log.debug("\tCreating new volume $volumeName")
EngineResponseContent<Volume> volumeResponse = dockerClient.createVolume(volumeName)
volume = volumeResponse?.content
assert volume: "Error creating volume $volumeName, " + volumeResponse?.getStatus()?.text
log.debug("\t\tCreated volume:" + volume.name)
}

return volume
}

@Override
boolean setupDeployment() {


srcSyncer = DirectorySyncer.createSyncToVolume(srcCodePaths, srcCodeVolume.name, "-avh --chown=2001:2001")
allureContainer.created ?: allureContainer.createContainer()
allureContainer.startContainer()

jsmDeployment.setupDeployment(true, false)


}

public static class Builder {

private String jsmLicense
private String jsmBaseUrl
private String jsmVersion = "latest"
private String jsmJvmDebugPort = "5005"
private Boolean enableJsmDooD = false

private Map<String, String> appsToInstall = [:]

private String dockerHost
private String dockerCertPath

private ArrayList<String> srcCodePaths

Builder(String baseUrl, String jsmLicense, ArrayList<String> srcCodePaths) {
this.jsmLicense = jsmLicense
this.jsmBaseUrl = baseUrl
this.srcCodePaths = srcCodePaths
}

Builder setJsmVersion(String version) {
this.jsmVersion = version
return this
}

Builder setJsmJvmDebugPort(String port) {
this.jsmJvmDebugPort = port
return this
}

/**
* Enable Docker Outside of Docker by mounting "/var/run/docker.sock" in to jsm container
* @return
*/
Builder enableJsmDood() {
this.enableJsmDooD = true
return this
}


Builder addAppToInstall(String appUrl, String appLicense = "") {
this.appsToInstall.put(appUrl, appLicense)
return this
}

Builder setDockerHost(String host) {
this.dockerHost = host
return this
}

Builder setDockerCertPath(String certPath) {
this.dockerCertPath = certPath
return this
}


JsmDevDeployment build() {

JsmDevDeployment devDeployment = new JsmDevDeployment(jsmBaseUrl, dockerHost, dockerCertPath, srcCodePaths)

devDeployment.jsmDeployment.jsmContainer.containerImageTag = this.jsmVersion
devDeployment.jsmDeployment.jsmContainer.created ?: devDeployment.jsmDeployment.jsmContainer.enableJvmDebug(this.jsmJvmDebugPort)
devDeployment.jsmDeployment.setJiraLicense(this.jsmLicense)
devDeployment.jsmDeployment.jsmContainer.enableAppUpload()
if (enableJsmDooD) {
devDeployment.jsmDeployment.jsmContainer.prepareBindMount("/var/run/docker.sock", "/var/run/docker.sock", false)
}
devDeployment.jsmDeployment.appsToInstall = this.appsToInstall


return devDeployment

}

}


}


Loading

0 comments on commit 9e12884

Please sign in to comment.