run container tests in ci

This commit is contained in:
az 2021-02-25 19:57:06 +01:00
parent 03add093b4
commit 19469a07fd
19 changed files with 140 additions and 146 deletions

View file

@ -9,8 +9,6 @@ stages:
before_script:
- echo "---------- Start CI ----------"
- export GRADLE_USER_HOME=`pwd`/.gradle
- export repoUser="$repoUser"
- export repoPassword="$repoPassword"
- chmod +x gradlew
build:
@ -25,12 +23,28 @@ build:
test:
stage: test
image: docker:latest
services:
- docker:dind
dependencies:
- build
before_script:
- echo "---------- BEFORE -------------"
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
- ./gradlew test
- echo "---------- TEST -------------"
- apk update && apk add bash openjdk11
- export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
- docker build --pull -t "$CI_REGISTRY_IMAGE" .
- docker run --privileged -dit --name provs_test -v /var/run/docker.sock:/var/run/docker.sock $CI_REGISTRY_IMAGE
- docker inspect -f '{{.State.Running}}' provs_test
- ./gradlew test -Dtestdockerwithoutsudo=true
artifacts:
when: on_failure
paths:
- build/reports/tests/test
reports:
junit: build/test-results/test/TEST-*.xml
jar:
stage: jar
@ -46,7 +60,7 @@ publish:
stage: publish
script:
- echo "---------- publish ----------"
- ./gradlew -PrepoUser="$repoUser" -PrepoPassword="$repoPassword" publish
- ./gradlew publish
after_script:
- echo "---------- End CI ----------"

View file

@ -22,8 +22,19 @@ repositories {
}
test {
// set properties for the tests
def propertiesForTests = ["testdockerwithoutsudo"]
for (def prop : propertiesForTests) {
def value = System.getProperty(prop)
if (value != null) {
systemProperty prop, value
}
}
useJUnitPlatform {
excludeTags('containertest')
if (System.getenv("CI_JOB_TOKEN") != null) {
excludeTags('containernonci')
}
}
}

View file

@ -1,4 +1 @@
kotlin.code.style=official
#avoid nexus error "Cannot upload checksum for snapshot-maven-metadata.xml. Remote repository doesn't support sha-512. Error: Could not PUT ..."
systemProp.org.gradle.internal.publish.checksums.insecure=true

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -71,6 +71,7 @@ open class Prov protected constructor(private val processor: Processor, val name
return handle(ResultMode.FAILEXIT) { a() }
}
// todo: add sudo and update test
fun inContainer(containerName: String, a: Prov.() -> ProvResult): ProvResult {
runInContainerWithName = containerName
val res = handle(ResultMode.ALL) { a() }

View file

@ -17,7 +17,10 @@ data class ProvResult(val success: Boolean,
}
fun toShortString() : String {
return "ProvResult:: ${if (success) "Succeeded" else "FAILED"} -- ${if (!success && (out != null)) "Details: $out" else ""}"
return "ProvResult:: ${if (success) "Succeeded" else "FAILED"} -- " +
if (!success)
(if (out != null) "Details: $out " else "" +
if (err != null) " Error: " + err else "") else ""
}
}

View file

@ -18,19 +18,20 @@ import io.provs.processors.ContainerStartMode
fun Prov.provideContainer(
containerName: String,
imageName: String = "ubuntu",
startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE
startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE,
sudo: Boolean = true
) : ProvResult {
if (this is UbuntuProv) {
return this.provideContainerPlatform(containerName, imageName, startMode)
return this.provideContainerPlatform(containerName, imageName, startMode, sudo)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}
}
fun Prov.containerRuns(containerName: String) : Boolean {
fun Prov.containerRuns(containerName: String, sudo: Boolean = true) : Boolean {
if (this is UbuntuProv) {
return this.containerRunsPlatform(containerName)
return this.containerRunsPlatform(containerName, sudo)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}
@ -38,38 +39,30 @@ fun Prov.containerRuns(containerName: String) : Boolean {
fun Prov.runContainer(
containerName: String = "defaultProvContainer",
imageName: String = "ubuntu"
containerName: String = "provs_default",
imageName: String = "ubuntu",
sudo: Boolean = true
) : ProvResult {
if (this is UbuntuProv) {
return this.runContainerPlatform(containerName, imageName)
return this.runContainerPlatform(containerName, imageName, sudo)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}
}
fun Prov.containerSh(containerName: String, cmd: String) : ProvResult {
fun Prov.dockerBuildImage(image: DockerImage, skipIfExisting: Boolean = true, sudo: Boolean = true) : ProvResult {
if (this is UbuntuProv) {
return this.containerShPlatform(containerName, cmd)
return this.dockerBuildImagePlatform(image, skipIfExisting, sudo)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}
}
fun Prov.dockerBuildImage(image: DockerImage, skipIfExisting: Boolean = true) : ProvResult {
fun Prov.dockerImageExists(imageName: String, sudo: Boolean = true) : Boolean {
if (this is UbuntuProv) {
return this.dockerBuildImagePlatform(image, skipIfExisting)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}
}
fun Prov.dockerImageExists(imageName: String) : Boolean {
if (this is UbuntuProv) {
return this.dockerImageExistsPlatform(imageName)
return this.dockerImageExistsPlatform(imageName, sudo)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}
@ -77,10 +70,11 @@ fun Prov.dockerImageExists(imageName: String) : Boolean {
fun Prov.exitAndRmContainer(
containerName: String
containerName: String,
sudo: Boolean = true
) : ProvResult {
if (this is UbuntuProv) {
return this.exitAndRmContainerPlatform(containerName)
return this.exitAndRmContainerPlatform(containerName, sudo)
} else {
throw RuntimeException("docker not yet supported for " + (this as UbuntuProv).javaClass)
}

View file

@ -12,54 +12,54 @@ import io.provs.processors.ContainerStartMode
fun UbuntuProv.provideContainerPlatform(
containerName: String,
imageName: String = "ubuntu",
startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE
startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE,
sudo: Boolean = true
): ProvResult = requireLast {
val dockerCmd = if (sudo) "sudo docker " else "docker "
if (startMode == ContainerStartMode.CREATE_NEW_KILL_EXISTING) {
exitAndRmContainer(containerName)
}
if ((startMode == ContainerStartMode.CREATE_NEW_KILL_EXISTING) || (startMode == ContainerStartMode.CREATE_NEW_FAIL_IF_EXISTING)) {
if (!cmd(
"sudo docker run -dit --name=$containerName $imageName"
).success
) {
if (!cmd(dockerCmd + "run -dit --name=$containerName $imageName").success) {
throw RuntimeException("could not start docker")
}
} else if (startMode == ContainerStartMode.USE_RUNNING_ELSE_CREATE) {
val r =
cmd("sudo docker inspect -f '{{.State.Running}}' $containerName")
cmd(dockerCmd + "inspect -f '{{.State.Running}}' $containerName")
if (!r.success || "false\n" == r.out) {
cmd("sudo docker rm -f $containerName")
cmd("sudo docker run -dit --name=$containerName $imageName")
cmd(dockerCmd + "rm -f $containerName")
cmd(dockerCmd + "run -dit --name=$containerName $imageName")
}
}
ProvResult(containerRuns(containerName))
ProvResult(containerRuns(containerName, sudo))
}
fun UbuntuProv.containerRunsPlatform(containerName: String): Boolean {
return cmdNoEval("sudo docker inspect -f '{{.State.Running}}' $containerName").out?.equals("true\n") ?: false
fun UbuntuProv.containerRunsPlatform(containerName: String, sudo: Boolean = true): Boolean {
val dockerCmd = if (sudo) "sudo docker " else "docker "
return cmdNoEval(dockerCmd + "inspect -f '{{.State.Running}}' $containerName").out?.equals("true\n") ?: false
}
fun UbuntuProv.runContainerPlatform(
containerName: String = "defaultProvContainer",
imageName: String = "ubuntu"
imageName: String = "ubuntu",
sudo: Boolean = true
) = def {
cmd("sudo docker run -dit --name=$containerName $imageName")
val dockerCmd = if (sudo) "sudo docker " else "docker "
cmd(dockerCmd + "run -dit --name=$containerName $imageName")
}
fun UbuntuProv.containerExecPlatform(containerName: String, cmd: String) = def {
cmd("sudo docker exec $containerName $cmd")
fun UbuntuProv.containerExecPlatform(containerName: String, cmd: String, sudo: Boolean = true) = def {
val dockerCmd = if (sudo) "sudo docker " else "docker "
cmd(dockerCmd + "exec $containerName $cmd")
}
fun UbuntuProv.containerShPlatform(containerName: String, cmd: String) = def {
containerExecPlatform(containerName, "sh -c \"${cmd.escapeDoubleQuote()}\"")
}
fun UbuntuProv.dockerBuildImagePlatform(image: DockerImage, skipIfExisting: Boolean): ProvResult {
fun UbuntuProv.dockerBuildImagePlatform(image: DockerImage, skipIfExisting: Boolean, sudo: Boolean): ProvResult {
val dockerCmd = if (sudo) "sudo docker " else "docker "
if (skipIfExisting && dockerImageExists(image.imageName())) {
return ProvResult(true)
@ -73,20 +73,25 @@ fun UbuntuProv.dockerBuildImagePlatform(image: DockerImage, skipIfExisting: Bool
cmd("cd $path && printf '${image.imageText().escapeSingleQuote()}' > Dockerfile")
return cmd("cd $path && sudo docker build --tag ${image.imageName()} .")
return cmd("cd $path && "+dockerCmd+"build --tag ${image.imageName()} .")
}
fun UbuntuProv.dockerImageExistsPlatform(imageName: String): Boolean {
return (cmd("sudo docker images $imageName -q").out != "")
fun UbuntuProv.dockerImageExistsPlatform(imageName: String, sudo: Boolean): Boolean {
val dockerCmd = if (sudo) "sudo docker " else "docker "
return (cmd(dockerCmd + "images $imageName -q").out != "")
}
fun UbuntuProv.exitAndRmContainerPlatform(
containerName: String
containerName: String,
sudo: Boolean
) = requireAll {
val dockerCmd = if (sudo) "sudo docker " else "docker "
if (containerRuns(containerName)) {
cmd("sudo docker stop $containerName")
cmd(dockerCmd + "stop $containerName")
}
cmd("sudo docker rm $containerName")
cmd(dockerCmd + "rm $containerName")
}

View file

@ -1,7 +1,6 @@
package io.provs.processors
import io.provs.Prov
import io.provs.docker.containerSh
import io.provs.docker.provideContainer
import io.provs.escapeAndEncloseByDoubleQuoteForShell
import io.provs.platforms.SHELL
@ -23,44 +22,31 @@ open class ContainerUbuntuHostProcessor(
private val dockerImage: String = "ubuntu",
@Suppress("unused") // suppress false positive warning
private val startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE,
private val endMode: ContainerEndMode = ContainerEndMode.KEEP_RUNNING
private val endMode: ContainerEndMode = ContainerEndMode.KEEP_RUNNING,
@Suppress("unused") // suppress false positive warning
private val sudo: Boolean = true
) : Processor {
private val dockerCmd = if (sudo) "sudo docker " else "docker "
private var localExecution = LocalProcessor()
private var a = Prov.newInstance(name = "ContainerUbuntuHostProcessor")
private var a = Prov.newInstance(name = "LocalProcessor for Docker operations")
init {
val r = a.provideContainer(containerName, dockerImage, startMode)
val r = a.provideContainer(containerName, dockerImage, startMode, sudo)
if (!r.success)
throw RuntimeException("Could not start docker image: " + r.toShortString(), r.exception)
throw RuntimeException("Could not start docker image: " + r.toString(), r.exception)
}
override fun x(vararg args: String): ProcessResult {
return localExecution.x("sh", "-c", "sudo docker exec $containerName " + buildCommand(*args))
return localExecution.x("sh", "-c", dockerCmd + "exec $containerName " + buildCommand(*args))
}
override fun xNoLog(vararg args: String): ProcessResult {
return localExecution.xNoLog("sh", "-c", "sudo docker exec $containerName " + buildCommand(*args))
}
fun installSudo(): ContainerUbuntuHostProcessor {
a.containerSh(containerName, "apt-get update")
a.containerSh(containerName, "apt-get -y install sudo")
return this
}
fun addAndSwitchToUser(user: String = "testuser"): ContainerUbuntuHostProcessor {
a.containerSh(containerName,"sudo useradd -m $user && echo '$user:$user' | chpasswd && adduser $user sudo")
a.containerSh(containerName,"echo '$user ALL=(ALL:ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/$user")
a.containerSh(containerName,"sudo su $user")
a.containerSh(containerName,"cd /home/$user")
a.containerSh(containerName,"mkdir $user && cd $user")
return this
return localExecution.xNoLog("sh", "-c", dockerCmd + "exec $containerName " + buildCommand(*args))
}
fun exitAndRm() {
localExecution.x(SHELL, "-c", "sudo docker stop $containerName")
localExecution.x(SHELL, "-c", "sudo docker rm $containerName")
localExecution.x(SHELL, "-c", dockerCmd + "stop $containerName")
localExecution.x(SHELL, "-c", dockerCmd + "rm $containerName")
}
private fun quoteString(s: String): String {

View file

@ -1,7 +1,5 @@
package io.provs
import io.provs.processors.ContainerStartMode
import io.provs.processors.ContainerUbuntuHostProcessor
import io.provs.testconfig.tags.CONTAINERTEST
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Tag
@ -15,8 +13,9 @@ internal class ContainerProcessorTest {
@Test
@Tag(CONTAINERTEST)
fun cmd_works_with_echo() {
// given
val prov = Prov.newInstance(ContainerUbuntuHostProcessor("provs_test", startMode = ContainerStartMode.CREATE_NEW_KILL_EXISTING))
val prov = defaultTestContainer()
val text = "abc123!§$%&/#äöü"
// when
@ -32,7 +31,7 @@ internal class ContainerProcessorTest {
@Tag(CONTAINERTEST)
fun cmdNoLog_works_with_echo() {
// given
val prov = Prov.newInstance(ContainerUbuntuHostProcessor("provs_test", startMode = ContainerStartMode.CREATE_NEW_KILL_EXISTING))
val prov = defaultTestContainer()
val text = "abc123!§$%&/#äöü"
// when
@ -41,5 +40,7 @@ internal class ContainerProcessorTest {
// then
assert(res.success)
assertEquals(text + newline(), res.out)
// todo add check that cmd was not logged
}
}

View file

@ -72,6 +72,8 @@ internal class LocalProcessorTest {
// then
assert(res.success)
assertEquals( text + System.lineSeparator(), res.out)
// todo add check that cmd was not logged
}

View file

@ -2,6 +2,7 @@ package io.provs
import io.provs.docker.provideContainer
import io.provs.testconfig.tags.CONTAINERTEST
import io.provs.testconfig.tags.CONTAINER_NON_CI
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Tag
@ -408,10 +409,10 @@ internal class ProvTest {
@Test
@EnabledOnOs(OS.LINUX)
@Tag(CONTAINERTEST)
@Tag(CONTAINER_NON_CI)
fun inContainer_locally() {
// given
val containerName = "provs_testing"
val containerName = "provs_test"
local().provideContainer(containerName, "ubuntu")
fun Prov.inner() = def {

View file

@ -0,0 +1,20 @@
package io.provs
import io.provs.processors.ContainerStartMode
import io.provs.processors.ContainerUbuntuHostProcessor
val DEFAULT_START_MODE_TEST_CONTAINER = ContainerStartMode.USE_RUNNING_ELSE_CREATE
val testDockerWithSudo = !"true".equals(System.getProperty("testdockerwithoutsudo")?.toLowerCase())
const val defaultTestContainerName = "provs_test"
fun defaultTestContainer(): Prov {
return Prov.newInstance(
ContainerUbuntuHostProcessor(
defaultTestContainerName,
startMode = DEFAULT_START_MODE_TEST_CONTAINER,
sudo = testDockerWithSudo
)
)
}

View file

@ -20,7 +20,7 @@ internal class UtilsTest {
@Tag(CONTAINERTEST)
fun test_docker() {
// when
val res = docker().cmd("echo")
val res = defaultTestContainer().cmd("echo something")
// then
Assertions.assertEquals(true, res.success)

View file

@ -5,7 +5,7 @@ import io.provs.docker.containerRuns
import io.provs.docker.exitAndRmContainer
import io.provs.docker.runContainer
import io.provs.local
import io.provs.testconfig.tags.CONTAINERTEST
import io.provs.testconfig.tags.CONTAINER_NON_CI
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
@ -16,7 +16,7 @@ internal class UbuntuHostDockerKtTest {
@Test
@EnabledOnOs(OS.LINUX)
@Tag(CONTAINERTEST)
@Tag(CONTAINER_NON_CI)
fun runAndCheckAndExitContainer() {
// when
val containerName = "testContainer"

View file

@ -1,8 +1,6 @@
package io.provs.platformTest
import io.provs.Prov
import io.provs.testconfig.tags.CONTAINERTEST
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.EnabledOnOs
import org.junit.jupiter.api.condition.OS
@ -16,12 +14,11 @@ internal class UbuntuProvTests {
}
private fun outerPing() = prov.def {
ping("nu.nl")
ping("gitlab.com")
}
@Test
@EnabledOnOs(OS.LINUX)
@Tag(CONTAINERTEST)
fun that_ping_works() {
// when
val res = outerPing()
@ -66,14 +63,13 @@ internal class UbuntuProvTests {
@Test
@EnabledOnOs(OS.LINUX)
@Tag(CONTAINERTEST)
fun that_xec_works() {
// given
val a = Prov.defaultInstance()
// when
val res1 = a.xec("/usr/bin/printf", "hi")
val res2 = a.xec("/bin/ping", "-c", "2", "github.com")
val res2 = a.xec("/bin/ping", "-c", "2", "gitlab.com")
val res3 = a.xec("/bin/bash", "-c", "echo echoed")
// then

View file

@ -1,37 +0,0 @@
package io.provs.platformTest
import io.provs.getCallingMethodName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.EnabledOnOs
import org.junit.jupiter.api.condition.OS
@EnabledOnOs(OS.LINUX)
internal class UbuntuProvUnitTest {
// @Test
// fun that_cond_executes_true_case() {
// // given
// val x = mockk<LocalProcessor>()
// every { x.x(*anyVararg()) } returns ProcessResult(0)
//
// val a = Prov.newInstance(x,"Linux")
//
// // when
// a.cond( { true }, { xec("doit") })
// a.cond( { false }, { xec("dont") })
//
// // then
// verify { x.x("doit") }
// verify(exactly = 0) { x.x("dont") }
// }
@Test
fun that_callingStack_works() {
// when
val s = getCallingMethodName()
// then
assert(s == "that_callingStack_works")
}
}

View file

@ -1,6 +1,8 @@
package io.provs.processors
import io.provs.DEFAULT_START_MODE_TEST_CONTAINER
import io.provs.platforms.SHELL
import io.provs.testDockerWithSudo
import io.provs.testconfig.tags.CONTAINERTEST
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
@ -8,17 +10,14 @@ import org.junit.jupiter.api.condition.EnabledOnOs
import org.junit.jupiter.api.condition.OS.LINUX
internal class ContainerUbuntuHostProcessorTest {
class ContainerUbuntuHostProcessorTest {
@Test
@EnabledOnOs(LINUX)
@Tag(CONTAINERTEST)
fun test() {
if (System.getProperty("os.name") == "Linux") {
val processor = ContainerUbuntuHostProcessor("UbuntuHostContainerExecution", "ubuntu", ContainerStartMode.CREATE_NEW_KILL_EXISTING)
processor.installSudo()
processor.x(SHELL, "-c", "'cd /home && mkdir blabla'")
processor.exitAndRm()
}
val processor =
ContainerUbuntuHostProcessor("provs_ubuntuhost_test", "ubuntu", DEFAULT_START_MODE_TEST_CONTAINER, sudo = testDockerWithSudo)
processor.x(SHELL, "-c", "'cd /home && mkdir blabla'")
}
}

View file

@ -1,3 +1,4 @@
package io.provs.testconfig.tags
const val CONTAINERTEST = "containertest"
const val CONTAINER_NON_CI = "containernonci"